1 Document Header

1.1 options & settings

chunk options

CSS for scrollable output & Header colors

Turning scientific / Exponential numbers off

options(scipen = 999)

1.3 Loading libs

library(tidyverse)
library(ggthemes)
library(lubridate)
library(gghighlight)
library(streamgraph)
library(plotly)
library(highcharter)
library(glue)
library(gghalves)
library(htmlwidgets)
library(RColorBrewer)
library(viridis)
library(ggtext)

1.4 Creating & setting custom theme


theme_viny_bright <- function(){
  
  library(ggthemes)
  
  ggthemes::theme_fivethirtyeight() %+replace%
  
  theme(
    axis.title = element_text(),
    
    axis.text = element_text(size = 13),
    
    legend.text = element_text(size = 10),
    
    panel.background = element_rect(fill = "white"),
    
    plot.background = element_rect(fill = "white"),
    
    strip.background = element_blank(),
    
    legend.background = element_rect(fill = NA),
    
    legend.key = element_rect(fill = NA),

    plot.title = element_text(hjust = 0.5,
                              size = 19,
                              face = "bold"),
    plot.subtitle = element_text(hjust = 0.5, colour = "maroon")
      )
  
  }

theme_set(theme_viny_bright())

1.5 Loading data

df_ind <- read.csv("../data kaggle/covid_19_india.csv")
df_ind
str(df_ind)
'data.frame':   9291 obs. of  9 variables:
 $ ï..Sno                  : int  1 2 3 4 5 6 7 8 9 10 ...
 $ Date                    : chr  "30/01/20" "31/01/20" "01/02/20" "02/02/20" ...
 $ Time                    : chr  "6:00 PM" "6:00 PM" "6:00 PM" "6:00 PM" ...
 $ State.UnionTerritory    : chr  "Kerala" "Kerala" "Kerala" "Kerala" ...
 $ ConfirmedIndianNational : chr  "1" "1" "2" "3" ...
 $ ConfirmedForeignNational: chr  "0" "0" "0" "0" ...
 $ Cured                   : int  0 0 0 0 0 0 0 0 0 0 ...
 $ Deaths                  : int  0 0 0 0 0 0 0 0 0 0 ...
 $ Confirmed               : int  1 1 2 3 3 3 3 3 3 3 ...
summary(df_ind)
     ï..Sno         Date               Time           State.UnionTerritory ConfirmedIndianNational
 Min.   :   1   Length:9291        Length:9291        Length:9291          Length:9291            
 1st Qu.:2324   Class :character   Class :character   Class :character     Class :character       
 Median :4646   Mode  :character   Mode  :character   Mode  :character     Mode  :character       
 Mean   :4646                                                                                     
 3rd Qu.:6968                                                                                     
 Max.   :9291                                                                                     
 ConfirmedForeignNational     Cured             Deaths          Confirmed        
 Length:9291              Min.   :      0   Min.   :    0.0   Min.   :      0.0  
 Class :character         1st Qu.:    152   1st Qu.:    2.0   1st Qu.:    538.5  
 Mode  :character         Median :   4308   Median :   66.0   Median :   6832.0  
                          Mean   :  78633   Mean   : 1487.6   Mean   :  91839.8  
                          3rd Qu.:  57727   3rd Qu.:  926.5   3rd Qu.:  78856.0  
                          Max.   :1737080   Max.   :47827.0   Max.   :1859367.0  
df_ind <- df_ind %>% 
  mutate(Date = lubridate::dmy(Date),
         State.UnionTerritory = as.factor(State.UnionTerritory),
         # Time = lubridate::parse_date_time(Time, "I:M p"),
         Time = as.factor(Time),
         ConfirmedIndianNational = as.integer(ConfirmedIndianNational),
         ConfirmedForeignNational = as.integer(ConfirmedForeignNational)
         )

df_ind
summary(df_ind)
     ï..Sno          Date                  Time         State.UnionTerritory ConfirmedIndianNational
 Min.   :   1   Min.   :2020-01-30   10:00 AM:  27   Kerala       : 315      Min.   :  0.00         
 1st Qu.:2324   1st Qu.:2020-05-26   5:00 PM : 899   Delhi        : 283      1st Qu.:  1.00         
 Median :4646   Median :2020-07-30   6:00 PM : 600   Rajasthan    : 282      Median :  3.00         
 Mean   :4646   Mean   :2020-07-29   7:30 PM :  56   Haryana      : 281      Mean   : 12.19         
 3rd Qu.:6968   3rd Qu.:2020-10-04   8:00 AM :7653   Uttar Pradesh: 281      3rd Qu.: 13.00         
 Max.   :9291   Max.   :2020-12-09   8:30 PM :  28   Ladakh       : 278      Max.   :177.00         
                                     9:30 PM :  28   (Other)      :7571      NA's   :8845           
 ConfirmedForeignNational     Cured             Deaths          Confirmed        
 Min.   : 0.000           Min.   :      0   Min.   :    0.0   Min.   :      0.0  
 1st Qu.: 0.000           1st Qu.:    152   1st Qu.:    2.0   1st Qu.:    538.5  
 Median : 0.000           Median :   4308   Median :   66.0   Median :   6832.0  
 Mean   : 1.496           Mean   :  78633   Mean   : 1487.6   Mean   :  91839.8  
 3rd Qu.: 1.000           3rd Qu.:  57727   3rd Qu.:  926.5   3rd Qu.:  78856.0  
 Max.   :14.000           Max.   :1737080   Max.   :47827.0   Max.   :1859367.0  
 NA's   :8845                                                                    
df_ind <- df_ind %>% 
  mutate(CFR = round(Deaths / Confirmed * 100, digits = 2),
         Recovery_Rate = round(Cured / Confirmed * 100, digits = 2))

df_ind
df_ind <- df_ind %>% 
  group_by(State.UnionTerritory) %>% 
  mutate(Daily_confirmed = Confirmed - lag(Confirmed, default = 0),
         Daily_deaths = Deaths - lag(Deaths, default = 0),
         Daily_cured = Cured - lag(Cured, default = 0)) %>% 
  ungroup()

df_ind
df_ind$State.UnionTerritory %>% 
  table()
.
             Andaman and Nicobar Islands                           Andhra Pradesh 
                                     259                                      273 
                       Arunachal Pradesh                                    Assam 
                                     251                                      253 
                                   Bihar         Cases being reassigned to states 
                                     263                                       60 
                              Chandigarh                            Chandigarh*** 
                                     265                                        1 
                            Chhattisgarh                       Dadar Nagar Haveli 
                                     266                                       37 
Dadra and Nagar Haveli and Daman and Diu                              Daman & Diu 
                                     181                                        1 
                                   Delhi                                      Goa 
                                     283                                      259 
                                 Gujarat                                  Haryana 
                                     265                                      281 
                        Himachal Pradesh                        Jammu and Kashmir 
                                     264                                      276 
                               Jharkhand                                Karnataka 
                                     253                                      276 
                                  Kerala                                   Ladakh 
                                     315                                      278 
                          Madhya Pradesh                              Maharashtra 
                                     264                                      275 
                          Maharashtra***                                  Manipur 
                                       1                                      261 
                               Meghalaya                                  Mizoram 
                                     240                                      260 
                                Nagaland                                   Odisha 
                                     207                                      269 
                              Puducherry                                   Punjab 
                                     267                                      275 
                               Punjab***                                Rajasthan 
                                       1                                      282 
                                  Sikkim                               Tamil Nadu 
                                     200                                      278 
                               Telangana                             Telangana*** 
                                      45                                        1 
                               Telengana                             Telengana*** 
                                     236                                        1 
                                 Tripura                               Unassigned 
                                     247                                        3 
                           Uttar Pradesh                              Uttarakhand 
                                     281                                      270 
                             West Bengal 
                                     267 
df_ind %>% 
  filter(State.UnionTerritory %in% c("Chandigarh***","Maharashtra***","Telangana***","Telengana***"))

This is the code to replace *** States with original which we are not gonna use

df_ind <- df_ind %>% 
  mutate(State.UnionTerritory = replace(State.UnionTerritory,
                                        State.UnionTerritory %in% c("Telengana","Telengana***","Telangana***"),
                                        "Telangana"),
         State.UnionTerritory = replace(State.UnionTerritory,
                                        State.UnionTerritory %in% "Maharashtra***",
                                        "Maharashtra"),
         State.UnionTerritory = replace(State.UnionTerritory,
                                        State.UnionTerritory %in% "Chandigarh***",
                                        "Chandigarh"),
         State.UnionTerritory = replace(State.UnionTerritory,
                                        State.UnionTerritory %in% "Punjab***",
                                        "Punjab"),
         
         # to get rid of old replaced levels from factor columns
         State.UnionTerritory = as.character(State.UnionTerritory),
         State.UnionTerritory = as.factor(State.UnionTerritory)
  ) 

df_ind
df_ind <- df_ind %>% 
  mutate(State.UnionTerritory = replace(State.UnionTerritory,
                                        State.UnionTerritory %in% c("Telengana"),
                                        "Telangana"),
         State.UnionTerritory = replace(State.UnionTerritory,
                                        State.UnionTerritory %in% c("Telengana***"),
                                        "Telangana***"),
         
         # to get rid of old replaced levels from factor columns
         State.UnionTerritory = as.character(State.UnionTerritory),
         State.UnionTerritory = as.factor(State.UnionTerritory)
  ) 

df_ind

1.5.1 Adding population

df_ind %>% 
  pull(State.UnionTerritory) %>% 
  unique()
 [1] Kerala                                   Telangana                               
 [3] Delhi                                    Rajasthan                               
 [5] Uttar Pradesh                            Haryana                                 
 [7] Ladakh                                   Tamil Nadu                              
 [9] Karnataka                                Maharashtra                             
[11] Punjab                                   Jammu and Kashmir                       
[13] Andhra Pradesh                           Uttarakhand                             
[15] Odisha                                   Puducherry                              
[17] West Bengal                              Chhattisgarh                            
[19] Chandigarh                               Gujarat                                 
[21] Himachal Pradesh                         Madhya Pradesh                          
[23] Bihar                                    Manipur                                 
[25] Mizoram                                  Andaman and Nicobar Islands             
[27] Goa                                      Unassigned                              
[29] Assam                                    Jharkhand                               
[31] Arunachal Pradesh                        Tripura                                 
[33] Nagaland                                 Meghalaya                               
[35] Dadar Nagar Haveli                       Cases being reassigned to states        
[37] Sikkim                                   Daman & Diu                             
[39] Dadra and Nagar Haveli and Daman and Diu Telangana***                            
[41] Maharashtra***                           Chandigarh***                           
[43] Punjab***                               
43 Levels: Andaman and Nicobar Islands Andhra Pradesh Arunachal Pradesh Assam ... West Bengal

from: https://uidai.gov.in/images/state-wise-aadhaar-saturation.pdf

df_ind <- df_ind %>% 
  mutate(Population_State = case_when(State.UnionTerritory == "Kerala" ~ 35699443,
                                      State.UnionTerritory == "Telangana" ~ 39362732,
                                      State.UnionTerritory == "Delhi" ~ 18710922,
                                      State.UnionTerritory == "Rajasthan" ~ 81032689,
                                      State.UnionTerritory == "Uttar Pradesh" ~ 237882725,
                                      State.UnionTerritory == "Haryana" ~ 28204692,
                                      State.UnionTerritory == "Ladakh" ~ 289023,
                                      State.UnionTerritory == "Tamil Nadu" ~ 77841267,
                                      State.UnionTerritory == "Karnataka" ~ 67562686,
                                      State.UnionTerritory == "Maharashtra" ~ 123144223,
                                      State.UnionTerritory == "Punjab" ~ 30141373,
                                      State.UnionTerritory == "Jammu and Kashmir" ~ 13606320,
                                      State.UnionTerritory == "Andhra Pradesh" ~ 53903393,
                                      State.UnionTerritory == "Uttarakhand" ~ 11250858,
                                      State.UnionTerritory == "Odisha" ~ 46356334,
                                      State.UnionTerritory == "Puducherry" ~ 1413542,
                                      State.UnionTerritory == "West Bengal" ~ 99609303,
                                      State.UnionTerritory == "Chhattisgarh" ~ 29436231,
                                      State.UnionTerritory == "Chandigarh" ~ 1158473,
                                      State.UnionTerritory == "Gujarat" ~ 63872399,
                                      State.UnionTerritory == "Himachal Pradesh" ~ 7451955,
                                      State.UnionTerritory == "Madhya Pradesh" ~ 85358965,
                                      State.UnionTerritory == "Bihar" ~ 124799926,
                                      State.UnionTerritory == "Manipur" ~ 3091545,
                                      State.UnionTerritory == "Mizoram" ~ 1239244,
                                      State.UnionTerritory == "Andaman and Nicobar Islands " ~ 417036,  
                                      State.UnionTerritory == "Goa" ~ 1586250,
                                      State.UnionTerritory == "Assam" ~ 35607039,
                                      State.UnionTerritory == "Jharkand" ~ 38593948,
                                      State.UnionTerritory == "Arunachal Pradesh" ~ 1570458,    
                                      State.UnionTerritory == "Tripura" ~ 4169794,
                                      State.UnionTerritory == "Nagaland" ~ 2249695,
                                      State.UnionTerritory == "Meghalaya" ~ 3366710,
                                      State.UnionTerritory == "Dadar Nagar Haveli" ~ 615724,  
                                      State.UnionTerritory == "Sikkim" ~ 690251,
                                      State.UnionTerritory == "Daman & Diu" ~  615724)
         
         )

df_ind

1.5.1.1 Creating multiple gather/long columns

df_ind %>% 
  select(- ï..Sno) %>% 
  gather(key = "Daily_cases_type", value = "Cases_counts", 
         -c("State.UnionTerritory", "Date", "Time", "Population_State", 
            "ConfirmedIndianNational", "ConfirmedForeignNational", "CFR"
)) %>% 
  pull(Daily_cases_type) %>% 
  table()
.
      Confirmed           Cured Daily_confirmed     Daily_cured    Daily_deaths          Deaths 
           9291            9291            9291            9291            9291            9291 
  Recovery_Rate 
           9291 
df_ind_stacked_daily <- df_ind %>% 
  select(- c("ï..Sno","Confirmed","Cured","Deaths")) %>% 
  
  gather(key = "Daily_cases_type", value = "Daily_cases_counts", 
         -c("State.UnionTerritory", "Date", "Time", "Population_State", 
            "ConfirmedIndianNational", "ConfirmedForeignNational", "CFR","Recovery_Rate"
)) 
  
df_ind_stacked_daily
df_ind_stacked_cum <- df_ind %>% 
  select(- c("ï..Sno","Daily_confirmed","Daily_cured","Daily_deaths")) %>% 
  
  gather(key = "Cases_type", value = "Cases_counts", 
         -c("State.UnionTerritory", "Date", "Time", "Population_State", 
            "ConfirmedIndianNational", "ConfirmedForeignNational", "CFR","Recovery_Rate"
)) 
  
df_ind_stacked_cum
df_ind_stacked_rates <- df_ind %>% 
  select(- c("ï..Sno") )%>% 
  
  gather(key = "Rate_type", value = "Rate_value", 
         -c("State.UnionTerritory", "Date", "Time", "Population_State",
            "ConfirmedIndianNational", "ConfirmedForeignNational", 
            "Daily_confirmed", "Daily_cured", "Daily_deaths",
            "Confirmed", "Cured", "Deaths"))
  
df_ind_stacked_rates

1.5.2 Current Confirmed Cases by States

df_ind %>% 
  filter(Date == max(Date, na.rm = TRUE)) %>% 
  
  highcharter::data_to_hierarchical(
    group_vars = c(State.UnionTerritory),
    size_var = Confirmed,
    # colors = getOption("hicharter.color_palette")
) %>% 
  
  hchart(type = "treemap")
df_ind %>% 
  filter(Date == max(Date, na.rm = TRUE)) %>% 
  
  hchart(type = "treemap", 
         hcaes(x = State.UnionTerritory, value = Confirmed, color = Confirmed)
) %>% 
  hc_colorAxis(stops = color_stops(colors = viridis::inferno(10))) %>% 
  
  hc_title(text = "Relative size of Cumulative Confirmed Cases of Indian State/UT", align = "center")
df_ind %>%
  filter(Date == max(Date, na.rm = TRUE)) %>% 
  mutate(State.UnionTerritory = fct_reorder(State.UnionTerritory, Confirmed)) %>% 
  
  ggplot(aes(x = Confirmed, y = State.UnionTerritory, fill = State.UnionTerritory)) +
  geom_col() +
  theme_classic() +
  theme(legend.position = "none",
        axis.ticks.y = element_blank(),
        axis.line.y = element_blank()) +
  scale_x_continuous(labels = scales::comma_format()) +
  labs(title = glue("Cumulative Confirmed Cases as of {max(df_ind$Date, na.rm = TRUE)}"),
       caption = "Created by ViSa !!")

1.5.3 Over the time

1.5.3.1 Top States with total Confirmed cases

df_ind %>% 
  filter(!State.UnionTerritory %in% 
           c("Maharashtra***","Punjab***","Chandigarh***","Telangana***")) %>% 
  ggplot(aes(x = Date, y = Confirmed, col = State.UnionTerritory)) +
  geom_line(size = 1.1) +
  gghighlight(Confirmed > 500000) +
  labs(title = "States highlighted above confirmed cases: 500,000",
       subtitle = "Maharashtra tops the list",
       caption = "Created by ViSa !!")

df_ind %>% 
  filter(Date > as.Date("2020-04-01")) %>% 
  
  streamgraph(date = "Date", key = "State.UnionTerritory", value = "Confirmed") %>% 
  sg_fill_brewer("PuOr") %>% 
  sg_title("Confirmed cases State wise relative growth since Apr")
Confirmed cases State wise relative growth since Apr

1.5.3.2 Top States with Daily Confirmed cases

df_ind %>% 
  group_by(State.UnionTerritory) %>% 
  summarise(max_daily_Case = max(Daily_confirmed)) %>% 
  ungroup()
df_ind %>% 
  group_by(State.UnionTerritory) %>% 
  summarise(max_daily_Case = max(Daily_confirmed)) %>% 
  ungroup() %>% 
  summarise(max_of_max = max(max_daily_Case))
df_ind %>% 
  filter(!State.UnionTerritory %in% 
           c("Maharashtra***","Punjab***","Chandigarh***","Telangana***")) %>% 
  
  ggplot(aes(x = Date, y = Daily_confirmed, col = State.UnionTerritory)) +
  geom_line(size = .8) +
  gghighlight(Daily_confirmed > 4000) +
  labs(title = "Highlighted States with daily confirmed cases > 4000",
       caption = "Created by ViSa !!")

df_ind %>% 
  filter(!State.UnionTerritory %in% 
           c("Maharashtra***","Punjab***","Chandigarh***","Telangana***")) %>%
  
  ggplot(aes(x = Date, y = Daily_confirmed, col = State.UnionTerritory)) +
  geom_line() +
  scale_y_log10() +
  labs(title = "States highlighted with Daily Confirmed cases > 5000",
       subtitle = "In log scale",
       caption = "Created by ViSa !!",
       y = "log of Daily Confirmed")

  gghighlight(Daily_confirmed > 5000)
$predicates
<list_of<quosure>>

[[1]]
<quosure>
expr: ^Daily_confirmed > 5000
env:  global


$n
NULL

$max_highlight
[1] 5

$unhighlighted_params
list()

$use_group_by
NULL

$use_direct_label
NULL

$label_key
<quosure>
expr: ^NULL
env:  empty

$label_params
$label_params$fill
[1] "white"


$keep_scales
[1] FALSE

$calculate_per_facet
[1] FALSE

attr(,"class")
[1] "gg_highlighter"
df_ind %>% 
  filter(!State.UnionTerritory %in% 
           c("Maharashtra***","Punjab***","Chandigarh***","Telangana***")) %>% 
  filter(State.UnionTerritory != "Telangana") %>% 
  
  ggplot(aes(x = Date, y = Daily_confirmed, col = State.UnionTerritory)) +
  geom_line(size = .8) +
  gghighlight(Daily_confirmed > 4000) +
  labs(title = "States highlighted (- Telangana)",
       subtitle = "daily confirmed > 4000",
       caption = "Created by ViSa !!")

1.5.3.3 Daily Confirmed

df_ind %>% 
  filter(!State.UnionTerritory %in% 
           c("Maharashtra***","Punjab***","Chandigarh***","Telangana***")) %>%
  filter(State.UnionTerritory %in% (df_ind %>% 
                                      filter(Date == max(Date, na.rm = TRUE)) %>% 
                                      top_n(n = 10, wt = Confirmed) %>% 
                                      pull(State.UnionTerritory))
         ) %>% 
  mutate(State.UnionTerritory = fct_reorder(State.UnionTerritory, Daily_confirmed)) %>% 
  
  ggplot(aes(x = State.UnionTerritory, y = Daily_confirmed, 
             fill = State.UnionTerritory, col = State.UnionTerritory)) +
  geom_half_boxplot(side = "l", alpha = 0.7) +
  geom_half_violin(side = "r", alpha = 0.7) +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 90),
        legend.position = "none") +
  labs(title = "Daily Confirmed Cases Distribution of top 10 States",
       subtitle = "Maharashtra with highest median and point values",
       caption = "created by ViSa !!")

1.5.3.4 Daily Confirmed

df_ind %>% 
  filter(!State.UnionTerritory %in% 
           c("Maharashtra***","Punjab***","Chandigarh***","Telangana***")) %>%
  filter(State.UnionTerritory %in% (df_ind %>% 
                                      filter(Date == max(Date, na.rm = TRUE)) %>% 
                                      top_n(n = 10, wt = Deaths) %>% 
                                      pull(State.UnionTerritory))
         ) %>% 
  mutate(State.UnionTerritory = fct_reorder(State.UnionTerritory, Daily_deaths)) %>% 
  
  ggplot(aes(x = State.UnionTerritory, y = Daily_deaths, 
             fill = State.UnionTerritory, col = State.UnionTerritory)) +
  geom_half_boxplot(side = "l", alpha = 0.7) +
  geom_half_point(side = "r") +
  # geom_jitter(alpha = 0.7) +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 90),
        legend.position = "none") +
  labs(title = "Daily Deaths Distribution of top 10 States",
       subtitle = "Maharashtra with highest median and point values",
       caption = "created by ViSa !!")

CFR

df_ind %>% 
  filter(!State.UnionTerritory %in% 
           c("Maharashtra***","Punjab***","Chandigarh***","Telangana***")) %>%
  
  ggplot(aes(x = Date, y = CFR, col = State.UnionTerritory)) +
  geom_line() +
  labs(title = "CFR of Indian States from beignning",
       y = "CFR (%)")

df_ind %>% 
  filter(!State.UnionTerritory %in% 
           c("Maharashtra***","Punjab***","Chandigarh***","Telangana***")) %>%
  filter(Date > ymd("2020-05-31")) %>% 
  
  ggplot(aes(x = Date, y = CFR, col = State.UnionTerritory)) +
  geom_line() +
  labs(title = "CFR of Indian States/UT",
       subtitle = "June onwards", 
       y = "CFR (%)",
       caption = "created by ViSa !!")

df_ind %>% 
  filter(!State.UnionTerritory %in% 
           c("Maharashtra***","Punjab***","Chandigarh***","Telangana***")) %>%
  filter(Date > as.Date("2020-05-31")) %>% 
  
  ggplot(aes(x = Date, y = CFR, col = State.UnionTerritory)) +
  geom_line(size = 0.8) +
  scale_color_tableau() +
  gghighlight(CFR > 3) +
  labs(title = "CFR of States/UT with CFR > 3%",
       subtitle = "(June onwards)",
       y = "CFR (%)",
       caption = "created by ViSa !!")

df_ind %>% 
  filter(!State.UnionTerritory %in% 
           c("Maharashtra***","Punjab***","Chandigarh***","Telangana***")) %>%
  filter(Date > as.Date("2020-05-31")) %>% 
  
  ggplot(aes(x = Date, y = CFR, col = State.UnionTerritory)) +
  geom_line(size = 0.8) +
  scale_color_tableau() +
  gghighlight(State.UnionTerritory %in% c("Delhi","Maharashtra","Karnatka",
                                          "Uttar Pradesh","Haryana","West Bengal")) +
  labs(title = "CFR of States June onwards",
       subtitle = "Highlighted for selected States",
       y = "CFR (%)",
       caption = "created by ViSa !!")

df_ind %>% 
  filter(!State.UnionTerritory %in% 
           c("Maharashtra***","Punjab***","Chandigarh***","Telangana***")) %>%
  filter(Date > as.Date("2020-05-31")) %>% 
  
  ggplot(aes(x = Date, y = CFR, col = State.UnionTerritory)) +
  geom_line(size = 0.8) +
  scale_color_tableau() +
  gghighlight(State.UnionTerritory %in% 
                (df_ind %>% 
                filter(Date == max(Date, na.rm = TRUE)) %>% 
                top_n(n = 7, wt = CFR) %>%
                pull(State.UnionTerritory)  )
                ) +
  theme_minimal() +
  labs(title = glue("Highlighted Top 7 States/UT with highest CFR as of {max(df_ind$Date, na.rm=TRUE)}"),
       subtitle = "CFR of States June onwards",
       y = "CFR (%)",
       caption = "Created by ViSa !!")

ggsave(filename = "Top7_CFR_Indian_States.jpg", dpi = 300, height = 8, width = 10)
stream_CFR <- df_ind %>% 
  filter(!State.UnionTerritory %in% 
           c("Maharashtra***","Punjab***","Chandigarh***","Telangana***")) %>%
  
  streamgraph(date = "Date", key = "State.UnionTerritory", value = "CFR") %>% 
  sg_fill_tableau() %>% 
  # sg_fill_brewer("BuPu") %>% 
  sg_title("Case Fatality Rate of all Indian States across 2020")

stream_CFR
Case Fatality Rate of all Indian States across 2020
stream_confirmed <- df_ind %>% 
  filter(!State.UnionTerritory %in% 
           c("Maharashtra***","Punjab***","Chandigarh***","Telangana***")) %>% 
  
  streamgraph(date = "Date", key = "State.UnionTerritory", value = "Daily_confirmed") %>% 
  sg_fill_tableau() %>% 
  sg_title(title = "Flow of Daily confirmed cases by all Indian States across 2020")

stream_confirmed
Flow of Daily confirmed cases by all Indian States across 2020

Adding annotations, legend from: https://blogs.worldbank.org/opendata/interactive-product-export-streamgraphs-data360r-now-cran

stream_deaths <- df_ind %>% 
  filter(!State.UnionTerritory %in% 
           c("Maharashtra***","Punjab***","Chandigarh***","Telangana***")) %>% 
  
  streamgraph(date = "Date", key = "State.UnionTerritory", value = "Daily_deaths") %>% 
  sg_fill_tableau() %>% 
  sg_title(title = "Flow of Daily covid deaths reported by all Indian States across 2020")#%>%
  # sg_annotate(label="Maharashtra", x=as.Date("2020-09-20"), y=1000, color="#ffffff", size=18) #%>% 
  # sg_legend(show=TRUE, label= "State: ")%>%

stream_deaths
Flow of Daily covid deaths reported by all Indian States across 2020

Not saving shiny.tag object

htmlwidgets::saveWidget(stream_CFR, "stream_CFR_states.html")
class(stream_CFR)
[1] "shiny.tag"

1.5.4 top 5 States of confirmed cases

df_ind_stacked_cum %>% 
  mutate(month = lubridate::month(Date, label = TRUE, abbr = TRUE)) %>% 
  # filter(Cases_type == "Confirmed") %>% 
  group_by(State.UnionTerritory, Cases_type, month) %>% 
  summarise(Cases_counts = max(Cases_counts, na.rm = TRUE))

1.5.5 Top 7 confirmed cases States

df_ind_stacked_cum %>% 
  filter(!State.UnionTerritory %in% 
           c("Maharashtra***","Punjab***","Chandigarh***","Telangana***", "Cases being reassigned to states")) %>%
  mutate(month = lubridate::month(Date, label = TRUE, abbr = TRUE)) %>% 
  filter(Cases_type == "Confirmed") %>%
  group_by(State.UnionTerritory, month) %>% 
  summarise(Cases_counts = max(Cases_counts, na.rm = TRUE)) %>% 
  group_by(month) %>% 
  top_n(n = 7, wt = Cases_counts) %>% 
  ungroup() %>% 
  mutate(State.UnionTerritory = fct_reorder(State.UnionTerritory, Cases_counts, max)) %>% 

ggplot(aes(x = Cases_counts, y = State.UnionTerritory, 
           fill = State.UnionTerritory, group = State.UnionTerritory )) +
  geom_col() +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 90),
        legend.position = "none") +
  facet_wrap(~month) +
  # coord_cartesian(clip = "off") +
  scale_fill_tableau(palette = "Tableau 20") +
  labs(title = "Confirmed cases: Top 7 States/UT each month",
       caption = "Created by ViSa !!") +
  scale_x_continuous(label = scales::comma_format())

1.5.6 Top 7 ConfirmedperCapita States May onwards

df_ind_stacked_cum %>% 
  filter(!State.UnionTerritory %in% 
           c("Maharashtra***","Punjab***","Chandigarh***","Telangana***", "Cases being reassigned to states"),
         Date > as.Date("2020-04-30")
         ) %>%
  mutate(month = lubridate::month(Date, label = TRUE, abbr = TRUE)) %>% 
  filter(Cases_type == "Confirmed") %>%
  mutate(ConfPercapita = Cases_counts/Population_State) %>% 
  
  group_by(State.UnionTerritory, month) %>% 
  summarise(ConfPercapita = max(ConfPercapita, na.rm = TRUE)) %>% 
  group_by(month) %>% 
  top_n(n = 7, wt = ConfPercapita) %>% 
  ungroup() %>% 
  mutate(State.UnionTerritory = fct_reorder(State.UnionTerritory, ConfPercapita, max)) %>% 

ggplot(aes(x = ConfPercapita, y = State.UnionTerritory, 
           fill = State.UnionTerritory, group = State.UnionTerritory )) +
  geom_col() +
  theme_clean() +
  theme(axis.text.x = element_text(angle = 90),
        legend.position = "none") +
  facet_wrap(~month) +
  # coord_cartesian(clip = "off") +
  labs(title = "Confirmed cases Percapita: Top 7 States/UT each month (May onwards)",
       subtitle = "Delhi is worst Performed among Metro states & overall recently !!", 
       y = "",
       caption = "Created by ViSa !!") +
  scale_x_continuous(labels = scales::percent_format()) +
  scale_fill_brewer(palette = "Paired")

1.5.7 Top 7 CFR States

Top 7 based on: State’s CFR max spike in each month

Exporting csv

write.csv(df_ind_stacked_cum, "df_ind_stacked_cum.csv", row.names = FALSE) 
df_ind_stacked_cum %>% 
  filter(!State.UnionTerritory %in% 
           c("Maharashtra***","Punjab***","Chandigarh***","Telangana***", "Cases being reassigned to states")) %>%
  mutate(month = lubridate::month(Date, label = TRUE, abbr = TRUE)) %>% 
  filter(Cases_type == "Confirmed") %>%
  group_by(State.UnionTerritory, month) %>% 
  summarise(CFR = max(CFR, na.rm = TRUE)) %>% 
  group_by(month) %>% 
  top_n(n = 7, wt = CFR) %>% 
  ungroup() %>% 
  mutate(State.UnionTerritory = fct_reorder(State.UnionTerritory, CFR, max)) %>% 

ggplot(aes(x = CFR, y = State.UnionTerritory, 
           fill = State.UnionTerritory, group = State.UnionTerritory )) +
  geom_col() +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 90),
        legend.position = "none") +
  facet_wrap(~month) +
  # coord_cartesian(clip = "off") +
  scale_fill_tableau(palette = "Tableau 20") +
  labs(title = "Case Fatality Ratio: Top 7 States each month",
       subtitle = "Top 7 based on State's CFR max spike in each month",
       x = "CFR (%)",
       caption = "Created by ViSa !!")

1.5.8 Top 7 CFR States May onwards

df_ind_stacked_cum %>% 
  filter(!State.UnionTerritory %in% 
           c("Maharashtra***","Punjab***","Chandigarh***","Telangana***", "Cases being reassigned to states"),
         Date > as.Date("2020-04-30")
         ) %>%
  mutate(month = lubridate::month(Date, label = TRUE, abbr = TRUE)) %>% 
  filter(Cases_type == "Confirmed") %>%
  group_by(State.UnionTerritory, month) %>% 
  summarise(CFR = max(CFR, na.rm = TRUE)) %>% 
  group_by(month) %>% 
  top_n(n = 7, wt = CFR) %>% 
  ungroup() %>% 
  mutate(State.UnionTerritory = fct_reorder(State.UnionTerritory, CFR, max)) %>% 

ggplot(aes(x = CFR, y = State.UnionTerritory, 
           fill = State.UnionTerritory, group = State.UnionTerritory )) +
  geom_col() +
  theme_clean() +
  theme(axis.text.x = element_text(angle = 90),
        legend.position = "none") +
  facet_wrap(~month) +
  # coord_cartesian(clip = "off") +
  scale_fill_tableau(palette = "Tableau 20") +
  labs(title = "CFR: Top 7 States each month (May onwards)",
       subtitle = "Top 7 based on State's max CFR spike in each month",
       x = "CFR (%)",
       caption = "Created by ViSa !!")

1.5.9 Top 7 States Death

based on State’s max spike in each month


df_ind_stacked_cum %>% 
  filter(!State.UnionTerritory %in% 
           c("Maharashtra***","Punjab***","Chandigarh***","Telangana***", 
             "Cases being reassigned to states")
         ) %>%
  mutate(month = lubridate::month(Date, label = TRUE, abbr = TRUE)) %>% 
  filter(Cases_type == "Deaths") %>%
  group_by(State.UnionTerritory, month) %>%
  
  summarise(Deaths = max(Cases_counts, na.rm = TRUE)) %>% 
  group_by(month) %>% 
  top_n(n = 7, wt = Deaths) %>% 
  ungroup() %>% 
  mutate(State.UnionTerritory = fct_reorder(State.UnionTerritory, Deaths, max)) %>% 

ggplot(aes(x = Deaths, y = State.UnionTerritory, 
           fill = State.UnionTerritory, group = State.UnionTerritory )) +
  geom_col() +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 90),
        legend.position = "none") +
  facet_wrap(~month) +
  # coord_cartesian(clip = "off") +
  scale_fill_tableau(palette = "Tableau 20") +
  labs(title = "Deaths: Top 7 States each month",
       subtitle = "Top 7 based on State's max Death spike in each month",
       caption = "Created by ViSa !!") +
  scale_x_continuous(labels = scales::comma_format()) 

df_ind_stacked_cum %>% pull(Cases_type) %>% table()
.
Confirmed     Cured    Deaths 
     9291      9291      9291 

1.5.10 Rate Line plots

(df_ind_stacked_rates$State.UnionTerritory %>% unique())[1:22]
 [1] Kerala            Telangana         Delhi             Rajasthan         Uttar Pradesh    
 [6] Haryana           Ladakh            Tamil Nadu        Karnataka         Maharashtra      
[11] Punjab            Jammu and Kashmir Andhra Pradesh    Uttarakhand       Odisha           
[16] Puducherry        West Bengal       Chhattisgarh      Chandigarh        Gujarat          
[21] Himachal Pradesh  Madhya Pradesh   
43 Levels: Andaman and Nicobar Islands Andhra Pradesh Arunachal Pradesh Assam ... West Bengal
df_ind_stacked_rates %>% 
  filter(!State.UnionTerritory %in% 
           c("Maharashtra***","Punjab***","Chandigarh***","Telangana***", 
             "Cases being reassigned to states", "Unassigned")
         ) %>%
  filter(State.UnionTerritory %in% (df_ind_stacked_rates$State.UnionTerritory %>% 
                                      unique())[1:22]) %>% 
  # mutate(month = lubridate::month(Date, label = TRUE, abbr = TRUE)) %>% 
  # filter(Cases_type == "Confirmed") %>%
  
  ggplot(aes(x = Date, y = Rate_value, col = Rate_type)) +
  geom_path() +
  facet_wrap(~State.UnionTerritory) +
  labs(title = "Recovery Rate & CFR of States: Part 1",
       y = "log of Rate(%)",
       caption = "Created by ViSa !!") +
  scale_y_log10()

df_ind_stacked_rates %>% 
  filter(!State.UnionTerritory %in% 
           c("Maharashtra***","Punjab***","Chandigarh***","Telangana***", 
             "Cases being reassigned to states", "Unassigned")
         ) %>%
  filter(State.UnionTerritory %in% (df_ind_stacked_rates$State.UnionTerritory %>% 
                                      unique())[23:43]) %>% 
  # mutate(month = lubridate::month(Date, label = TRUE, abbr = TRUE)) %>% 
  # filter(Cases_type == "Confirmed") %>%
  
  ggplot(aes(x = Date, y = Rate_value, col = Rate_type)) +
  geom_path() +
  facet_wrap(~State.UnionTerritory) +
  labs(title = "Recovery Rate & CFR of States: Part 2",
       y = "log of Rate(%)",
       caption = "Created by ViSa !!") +
  scale_y_log10()

df_ind_stacked_cum %>% 
  filter(!State.UnionTerritory %in% 
           c("Maharashtra***","Punjab***","Chandigarh***","Telangana***", 
             "Cases being reassigned to states")
         ) %>%
  mutate(month = lubridate::month(Date, label = TRUE, abbr = TRUE)) %>% 
  group_by(State.UnionTerritory, month, Cases_type) %>%
  
  summarise(Cases_counts = max(Cases_counts, na.rm = TRUE)) %>% 
  group_by(month, Cases_type) %>%
  top_n(n = 7, wt = Cases_counts) %>% #pull(State.UnionTerritory) %>% unique() 
  
  mutate(Cases_size = Cases_counts / sum(Cases_counts)) %>% 
  
  write.csv("df_ind_stacked_cum.csv", row.names = FALSE)

df_ind_stacked_cum %>% 
  filter(!State.UnionTerritory %in% 
           c("Maharashtra***","Punjab***","Chandigarh***","Telangana***", 
             "Cases being reassigned to states")
         ) %>%
  mutate(month = lubridate::month(Date, label = TRUE, abbr = TRUE)) %>% 
  group_by(State.UnionTerritory, month, Cases_type) %>%
  
  summarise(Cases_counts = max(Cases_counts, na.rm = TRUE)) %>% 
  group_by(month, Cases_type) %>%
  top_n(n = 7, wt = Cases_counts) %>% #pull(State.UnionTerritory) %>% unique() 
  
  mutate(Cases_size = Cases_counts / sum(Cases_counts)) %>% 
  mutate(State.UnionTerritory = fct_reorder(State.UnionTerritory, Cases_counts, max)) %>% 

  ggplot(aes(x = Cases_type, y = State.UnionTerritory)) +
  geom_point(shape = 21, # alpha = 0.6, 
             aes(size = Cases_size, color = Cases_type), fill = "#FFEBCD", stroke = 3) +
  theme_wsj() +
  theme(axis.text.x = element_text(angle = 90),
        title = element_text(size = 12),
        legend.position = "bottom"
        ) +
  facet_wrap(~month) +
  coord_cartesian(clip = "off") +
  scale_color_tableau(palette = "Tableau 10") +
  labs(title = "Top 7 States each month: Confirmed | Recovered | Deaths",
       subtitle = "Point Size varies based on respective month cases type within each category
       Top 7 based on State's max spike in each category each month",
       caption = "Created by ViSa !!") 

  # scale_x_continuous(labels = scales::comma_format()) 

df_ind_stacked_cum %>% 
  filter(!State.UnionTerritory %in% 
           c("Maharashtra***","Punjab***","Chandigarh***","Telangana***", 
             "Cases being reassigned to states")
         ) %>%
  mutate(month = lubridate::month(Date, label = TRUE, abbr = TRUE)) %>% 
  group_by(State.UnionTerritory, month, Cases_type) %>%
  
  summarise(Cases_counts = max(Cases_counts, na.rm = TRUE)) %>% 
  group_by(month, Cases_type) %>%
  top_n(n = 5, wt = Cases_counts) %>% #pull(State.UnionTerritory) %>% unique() 
  
  mutate(Cases_size = Cases_counts / sum(Cases_counts)) %>% 
  mutate(State.UnionTerritory = fct_reorder(State.UnionTerritory, Cases_counts, max)) %>% 

  ggplot(aes(x = Cases_type, y = State.UnionTerritory)) +
  geom_point(shape = 21, # alpha = 0.6, 
             aes(size = Cases_size, color = Cases_type), fill = "#FFEBCD", stroke = 3) +
  theme_wsj() +
  theme(axis.text.x = element_text(angle = 90),
        title = element_text(size = 12),
        legend.position = "bottom"
        ) +
  facet_wrap(~month) +
  coord_cartesian(clip = "off") +
  scale_color_tableau(palette = "Tableau 10") +
  labs(title = "Top 5 States each month: Confirmed | Recovered | Deaths",
       subtitle = "Point Size varies based on respective month cases type within each category
       Top 5 based on State's max spike in each category each month",
       caption = "Created by ViSa !!") 

  # scale_x_continuous(labels = scales::comma_format()) 

1.5.11 Top 3 States: CFR, Recovery rate each month

df_ind_stacked_rates %>% 
  filter(!State.UnionTerritory %in% 
           c("Maharashtra***","Punjab***","Chandigarh***","Telangana***", 
             "Cases being reassigned to states")
         ) %>%
  mutate(month = lubridate::month(Date, label = TRUE, abbr = TRUE)) %>% 
  
  group_by(State.UnionTerritory, month) %>% 
  summarise(New_CFR = round(sum(Deaths) / sum(Confirmed), digits = 4),
            New_Recovery_Rate = round(sum(Cured) / sum(Confirmed), digits = 4)) %>% 
  # mutate(New_CFR = replace(New_CFR, is.na(New_CFR), 0),
  #        New_Recovery_Rate = replace(New_Recovery_Rate, is.na(New_Recovery_Rate), 0)) %>% 
  
  gather(key = Rate_type, value = Rate_value, -c("State.UnionTerritory", "month")) %>% 
  mutate(Rate_type = replace(Rate_type, Rate_type == "New_CFR","CFR"),
         Rate_type = replace(Rate_type, Rate_type == "New_Recovery_Rate","Recovery_Rate"),
         Rate_type = as.factor(Rate_type)) %>% 

  group_by(month, Rate_type) %>%
  top_n(n = 3, wt = Rate_value) %>% 
  
  mutate(Cases_size = Rate_value / sum(Rate_value),
         Cases_size = replace(Cases_size, is.na(Cases_size), 0))  %>% 
  
  mutate(State.UnionTerritory = fct_reorder(State.UnionTerritory, Rate_value, max)) %>% 

  mutate(State.UnionTerritory = as.character(State.UnionTerritory), 
         State.UnionTerritory = replace(State.UnionTerritory,
                                        State.UnionTerritory == "Dadra and Nagar Haveli and Daman and Diu",
                                        "Dadra Daman& Diu"),
         State.UnionTerritory = as.factor(State.UnionTerritory)) %>% 
  ungroup() %>% #pull(State.UnionTerritory) %>% unique()

  ggplot(aes(x = Rate_type, y = State.UnionTerritory)) +
  geom_point(shape = 21, # alpha = 0.6, 
             aes(size = Cases_size, color = Rate_type), fill = "#FFEBCD", stroke = 3) +
  theme_wsj() +
  theme(axis.text.x = element_text(angle = 90),
        title = element_text(size = 12),
        legend.position = "bottom",
        axis.text.y = element_text(size = 12)
        ) +
  facet_wrap(~month) +
  coord_cartesian(clip = "off") +
  scale_color_tableau(palette = "Tableau 10") +
  labs(title = "Top 3 States each month: Case Fatality Rate | Recovery Rate",
       subtitle = "Point Size varies based on respective month cases type within each category",
       caption = "Created by ViSa !!")

  # scale_x_continuous(labels = scales::comma_format())  
  
df_ind_stacked_rates %>% 
  filter(!State.UnionTerritory %in% 
           c("Maharashtra***","Punjab***","Chandigarh***","Telangana***", 
             "Cases being reassigned to states")
         ) %>%
  mutate(month = lubridate::month(Date, label = TRUE, abbr = TRUE)) %>% 
  
  group_by(State.UnionTerritory, month) %>% 
  summarise(New_CFR = round(sum(Deaths) / sum(Confirmed), digits = 4),
            New_Recovery_Rate = round(sum(Cured) / sum(Confirmed), digits = 4)) %>% 
  # mutate(New_CFR = replace(New_CFR, is.na(New_CFR), 0),
  #        New_Recovery_Rate = replace(New_Recovery_Rate, is.na(New_Recovery_Rate), 0)) %>% 
  
  gather(key = Rate_type, value = Rate_value, -c("State.UnionTerritory", "month")) %>% 
  mutate(Rate_type = replace(Rate_type, Rate_type == "New_CFR","CFR"),
         Rate_type = replace(Rate_type, Rate_type == "New_Recovery_Rate","Recovery_Rate"),
         Rate_type = as.factor(Rate_type)) %>% 

  group_by(month, Rate_type) %>%
  # top_n(n = 3, wt = Rate_value) %>% 
  
  mutate(Cases_size = Rate_value / sum(Rate_value),
         Cases_size = replace(Cases_size, is.na(Cases_size), 0))  %>% 
  
  mutate(State.UnionTerritory = fct_reorder(State.UnionTerritory, Rate_value, max)) %>% 

  mutate(State.UnionTerritory = as.character(State.UnionTerritory), 
         State.UnionTerritory = replace(State.UnionTerritory,
                                        State.UnionTerritory == "Dadra and Nagar Haveli and Daman and Diu",
                                        "Dadra Daman& Diu"),
         State.UnionTerritory = as.factor(State.UnionTerritory)) %>% 
  ungroup() %>% 
  filter(month == "May", Rate_type == "CFR") %>% 
  arrange(desc(Rate_value))
df_ind_stacked_cum %>% 
  filter(!State.UnionTerritory %in% 
           c("Maharashtra***","Punjab***","Chandigarh***","Telangana***", "Cases being reassigned to states"),
         Date > as.Date("2020-04-30")
         ) %>%
  mutate(month = lubridate::month(Date, label = TRUE, abbr = TRUE)) %>% 
  filter(Cases_type == "Confirmed") %>%
  group_by(State.UnionTerritory, month) %>% 
  summarise(Cases_counts = max(Cases_counts, na.rm = TRUE),
            CFR = last(CFR)) %>% 
  group_by(month) %>% 
  top_n(n = 7, wt = CFR) %>% 
  ungroup() %>% 
  mutate(State.UnionTerritory = fct_reorder(State.UnionTerritory, CFR, max)) %>% 
  
  # filter(month == "May")

ggplot(aes(x = CFR, y = State.UnionTerritory, 
           fill = State.UnionTerritory, group = State.UnionTerritory )) +
  geom_col() +
  theme_clean() +
  theme(axis.text.x = element_text(angle = 90),
        legend.position = "none") +
  facet_wrap(~month) +
  # coord_cartesian(clip = "off") +
  scale_fill_tableau(palette = "Tableau 20") +
  labs(title = "CFR: Top 7 States each month (May onwards)",
       x = "CFR (%)",
       caption = "Created by ViSa !!")

df_ind_stacked_cum %>% 
  filter(!State.UnionTerritory %in% 
           c("Maharashtra***","Punjab***","Chandigarh***","Telangana***", "Cases being reassigned to states"),
         Date > as.Date("2020-04-30")
         ) %>%
  mutate(month = lubridate::month(Date, label = TRUE, abbr = TRUE)) %>% 
  filter(Cases_type == "Confirmed") %>%
  group_by(State.UnionTerritory, month) %>% 
  
  filter(month == "May", State.UnionTerritory == "Puducherry")

Finding Last dates of months

from: https://www.roelpeters.be/get-the-first-day-or-the-last-day-of-the-month-in-r-using-lubridate/

df_ind_stacked_cum %>% 
  pull(Date) %>% 
  unique() %>% 
  ymd() %>%
  ceiling_date("month")-1
  [1] "2020-01-31" "2020-01-31" "2020-02-29" "2020-02-29" "2020-02-29" "2020-02-29" "2020-02-29"
  [8] "2020-02-29" "2020-02-29" "2020-02-29" "2020-02-29" "2020-02-29" "2020-02-29" "2020-02-29"
 [15] "2020-02-29" "2020-02-29" "2020-02-29" "2020-02-29" "2020-02-29" "2020-02-29" "2020-02-29"
 [22] "2020-02-29" "2020-02-29" "2020-02-29" "2020-02-29" "2020-02-29" "2020-02-29" "2020-02-29"
 [29] "2020-02-29" "2020-02-29" "2020-02-29" "2020-03-31" "2020-03-31" "2020-03-31" "2020-03-31"
 [36] "2020-03-31" "2020-03-31" "2020-03-31" "2020-03-31" "2020-03-31" "2020-03-31" "2020-03-31"
 [43] "2020-03-31" "2020-03-31" "2020-03-31" "2020-03-31" "2020-03-31" "2020-03-31" "2020-03-31"
 [50] "2020-03-31" "2020-03-31" "2020-03-31" "2020-03-31" "2020-03-31" "2020-03-31" "2020-03-31"
 [57] "2020-03-31" "2020-03-31" "2020-03-31" "2020-03-31" "2020-03-31" "2020-03-31" "2020-04-30"
 [64] "2020-04-30" "2020-04-30" "2020-04-30" "2020-04-30" "2020-04-30" "2020-04-30" "2020-04-30"
 [71] "2020-04-30" "2020-04-30" "2020-04-30" "2020-04-30" "2020-04-30" "2020-04-30" "2020-04-30"
 [78] "2020-04-30" "2020-04-30" "2020-04-30" "2020-04-30" "2020-04-30" "2020-04-30" "2020-04-30"
 [85] "2020-04-30" "2020-04-30" "2020-04-30" "2020-04-30" "2020-04-30" "2020-04-30" "2020-04-30"
 [92] "2020-04-30" "2020-05-31" "2020-05-31" "2020-05-31" "2020-05-31" "2020-05-31" "2020-05-31"
 [99] "2020-05-31" "2020-05-31" "2020-05-31" "2020-05-31" "2020-05-31" "2020-05-31" "2020-05-31"
[106] "2020-05-31" "2020-05-31" "2020-05-31" "2020-05-31" "2020-05-31" "2020-05-31" "2020-05-31"
[113] "2020-05-31" "2020-05-31" "2020-05-31" "2020-05-31" "2020-05-31" "2020-05-31" "2020-05-31"
[120] "2020-05-31" "2020-05-31" "2020-05-31" "2020-05-31" "2020-06-30" "2020-06-30" "2020-06-30"
[127] "2020-06-30" "2020-06-30" "2020-06-30" "2020-06-30" "2020-06-30" "2020-06-30" "2020-06-30"
[134] "2020-06-30" "2020-06-30" "2020-06-30" "2020-06-30" "2020-06-30" "2020-06-30" "2020-06-30"
[141] "2020-06-30" "2020-06-30" "2020-06-30" "2020-06-30" "2020-06-30" "2020-06-30" "2020-06-30"
[148] "2020-06-30" "2020-06-30" "2020-06-30" "2020-06-30" "2020-06-30" "2020-06-30" "2020-07-31"
[155] "2020-07-31" "2020-07-31" "2020-07-31" "2020-07-31" "2020-07-31" "2020-07-31" "2020-07-31"
[162] "2020-07-31" "2020-07-31" "2020-07-31" "2020-07-31" "2020-07-31" "2020-07-31" "2020-07-31"
[169] "2020-07-31" "2020-07-31" "2020-07-31" "2020-07-31" "2020-07-31" "2020-07-31" "2020-07-31"
[176] "2020-07-31" "2020-07-31" "2020-07-31" "2020-07-31" "2020-07-31" "2020-07-31" "2020-07-31"
[183] "2020-07-31" "2020-07-31" "2020-08-31" "2020-08-31" "2020-08-31" "2020-08-31" "2020-08-31"
[190] "2020-08-31" "2020-08-31" "2020-08-31" "2020-08-31" "2020-08-31" "2020-08-31" "2020-08-31"
[197] "2020-08-31" "2020-08-31" "2020-08-31" "2020-08-31" "2020-08-31" "2020-08-31" "2020-08-31"
[204] "2020-08-31" "2020-08-31" "2020-08-31" "2020-08-31" "2020-08-31" "2020-08-31" "2020-08-31"
[211] "2020-08-31" "2020-08-31" "2020-08-31" "2020-08-31" "2020-08-31" "2020-09-30" "2020-09-30"
[218] "2020-09-30" "2020-09-30" "2020-09-30" "2020-09-30" "2020-09-30" "2020-09-30" "2020-09-30"
[225] "2020-09-30" "2020-09-30" "2020-09-30" "2020-09-30" "2020-09-30" "2020-09-30" "2020-09-30"
[232] "2020-09-30" "2020-09-30" "2020-09-30" "2020-09-30" "2020-09-30" "2020-09-30" "2020-09-30"
[239] "2020-09-30" "2020-09-30" "2020-09-30" "2020-09-30" "2020-09-30" "2020-09-30" "2020-09-30"
[246] "2020-10-31" "2020-10-31" "2020-10-31" "2020-10-31" "2020-10-31" "2020-10-31" "2020-10-31"
[253] "2020-10-31" "2020-10-31" "2020-10-31" "2020-10-31" "2020-10-31" "2020-10-31" "2020-10-31"
[260] "2020-10-31" "2020-10-31" "2020-10-31" "2020-10-31" "2020-10-31" "2020-10-31" "2020-10-31"
[267] "2020-10-31" "2020-10-31" "2020-10-31" "2020-10-31" "2020-10-31" "2020-10-31" "2020-10-31"
[274] "2020-10-31" "2020-10-31" "2020-10-31" "2020-11-30" "2020-11-30" "2020-11-30" "2020-11-30"
[281] "2020-11-30" "2020-11-30" "2020-11-30" "2020-11-30" "2020-11-30" "2020-11-30" "2020-11-30"
[288] "2020-11-30" "2020-11-30" "2020-11-30" "2020-11-30" "2020-11-30" "2020-11-30" "2020-11-30"
[295] "2020-11-30" "2020-11-30" "2020-11-30" "2020-11-30" "2020-11-30" "2020-11-30" "2020-11-30"
[302] "2020-11-30" "2020-11-30" "2020-11-30" "2020-11-30" "2020-11-30" "2020-12-31" "2020-12-31"
[309] "2020-12-31" "2020-12-31" "2020-12-31" "2020-12-31" "2020-12-31" "2020-12-31" "2020-12-31"
df_ind_stacked_cum %>% 
  filter(!State.UnionTerritory %in% 
           c("Maharashtra***","Punjab***","Chandigarh***","Telangana***", "Cases being reassigned to states"),
         Date > as.Date("2020-04-30")
         ) %>%
  mutate(month = lubridate::month(Date, label = TRUE, abbr = TRUE)) %>% 
  filter(Cases_type == "Confirmed",
         Date == ceiling_date(Date, "month") -1) %>%
  group_by(State.UnionTerritory, month) %>% 
  summarise(CFR = max(CFR, na.rm = TRUE)) %>% 
  group_by(month) %>% 
  top_n(n = 7, wt = CFR) %>% 
  ungroup() %>% 
  mutate(State.UnionTerritory = fct_reorder(State.UnionTerritory, CFR, max)) %>% 
  
  # filter(month == "May")

ggplot(aes(x = CFR, y = State.UnionTerritory, 
           fill = State.UnionTerritory, group = State.UnionTerritory )) +
  geom_col() +
  theme_clean() +
  theme(axis.text.x = element_text(angle = 90),
        legend.position = "none") +
  facet_wrap(~month) +
  # coord_cartesian(clip = "off") +
  scale_fill_tableau(palette = "Tableau 20") +
  labs(title = "CFR: Top 7 States each month (May onwards)",
       x = "CFR (%)",
       caption = "Created by ViSa !!")

df_ind_stacked_cum %>% 
  filter(!State.UnionTerritory %in% 
           c("Maharashtra***","Punjab***","Chandigarh***","Telangana***", "Cases being reassigned to states"),
         Date > as.Date("2020-04-30")
         ) %>%
  mutate(month = lubridate::month(Date, label = TRUE, abbr = TRUE)) %>% 
  filter(Cases_type == "Confirmed",
         Date == ceiling_date(Date, "month") -1) %>%
  group_by(State.UnionTerritory, month) %>% 
  summarise(CFR = max(CFR, na.rm = TRUE)) %>% 
  group_by(month) %>% #filter(month == "May") %>% arrange(desc(CFR))
  top_n(n = 7, wt = CFR) %>% 
  ungroup() %>% 
  mutate(State.UnionTerritory = fct_reorder(State.UnionTerritory, CFR, max)) %>% 
  
  # filter(month == "May")

ggplot(aes(x = CFR, y = State.UnionTerritory, 
           fill = State.UnionTerritory, group = State.UnionTerritory )) +
  geom_col() +
  theme_clean() +
  theme(axis.text.x = element_text(angle = 90),
        legend.position = "none") +
  facet_wrap(~month) +
  # coord_cartesian(clip = "off") +
  scale_fill_tableau(palette = "Tableau 20") +
  labs(title = "CFR: Top 7 States each month (May onwards)",
       x = "CFR (%)",
       caption = "Created by ViSa !!")

df_ind %>% filter(Daily_confirmed < 0)

Cumulative Numbers falls for few states and creates negative daily_confirmed cases.

df_ind %>% 
  filter(Date %in% c(as.Date("2020-03-09") : as.Date("2020-03-14")),
         State.UnionTerritory == "Maharashtra")

1.5.12 Conclusion

Calculation in CFR gets mismatched numbers when calculated by: 1. CFR calculated on daily basis and selected CFR on max(Confirmed) of month which should be correct if cumulative of figures is correct.

  1. CFR calculated by daily basis & CFR taken on last date of month by celing_date(“month”) -1 function.

1.5.13 Experiment - highcharter map data

from:

https://jkunst.com/highcharter/articles/maps.html

https://code.highcharts.com/

https://code.highcharts.com/mapdata/

https://jkunst.com/highcharter/reference/download_map_data.html

hcmap("countries/in/in-all")
df_ind_map <- get_data_from_map(download_map_data("https://code.highcharts.com/mapdata/countries/in/in-all.js"))
trying URL 'https://code.highcharts.com/mapdata/countries/in/in-all.js'
Content type 'text/javascript' length 52340 bytes (51 KB)
downloaded 51 KB
glimpse(df_ind_map)
Rows: 34
Columns: 20
$ `hc-group`    <chr> "admin1", "admin1", "admin1", "admin1", "admin1", "admin1", "admin1", "admin1...
$ `hc-middle-x` <dbl> 0.65, 0.59, 0.50, 0.56, 0.46, 0.46, 0.51, 0.59, 0.47, 0.56, 0.60, 0.63, 0.51,...
$ `hc-middle-y` <dbl> 0.81, 0.63, 0.74, 0.38, 0.64, 0.51, 0.34, 0.41, 0.60, 0.32, 0.47, 0.55, 0.48,...
$ `hc-key`      <chr> "in-py", "in-ld", "in-wb", "in-or", "in-br", "in-sk", "in-ct", "in-tn", "in-m...
$ `hc-a2`       <chr> "PY", "LD", "WB", "OR", "BR", "SK", "CT", "TN", "MP", "GU", "GA", "NL", "MN",...
$ labelrank     <chr> "2", "2", "2", "2", "2", "2", "2", "2", "2", "2", "2", "2", "2", "2", "2", "2...
$ hasc          <chr> "IN.PY", "IN.LD", "IN.WB", "IN.OR", "IN.BR", "IN.SK", "IN.CT", "IN.", "IN.MP"...
$ `alt-name`    <chr> "Pondicherry|Puduchcheri|Pondichéry", "Íles Laquedives|Laccadive|Minicoy and ...
$ `woe-id`      <chr> "20070459", "2345748", "2345761", "2345755", "2345742", "2345762", "20070464"...
$ fips          <chr> "IN22", "IN14", "IN28", "IN21", "IN34", "IN29", "IN37", "IN22", "IN35", "IN32...
$ `postal-code` <chr> "PY", "LD", "WB", "OR", "BR", "SK", "CT", "TN", "MP", NA, "GA", "NL", "MN", "...
$ name          <chr> "Puducherry", "Lakshadweep", "West Bengal", "Orissa", "Bihar", "Sikkim", "Chh...
$ country       <chr> "India", "India", "India", "India", "India", "India", "India", "India", "Indi...
$ `type-en`     <chr> "Union Territory", "Union Territory", "State", "State", "State", "State", "St...
$ region        <chr> "South", "South", "East", "East", "East", "East", "Central", "South", "Centra...
$ longitude     <chr> "79.7758", "72.7811", "87.7289", "84.4341", "85.8134", "88.4482", "82.3069", ...
$ `woe-name`    <chr> "Puducherry", "Lakshadweep", "West Bengal", "Orissa", "Bihar", "Sikkim", "Chh...
$ latitude      <chr> "10.9224", "11.2249", "23.0523", "20.625", "25.6853", "27.5709", "21.8044", "...
$ `woe-label`   <chr> "Puducherry, IN, India", "Lakshadweep, IN, India", "West Bengal, IN, India", ...
$ type          <chr> "Union Territor", "Union Territor", "State", "State", "State", "State", "Stat...
df_ind_map %>% 
  select(`hc-a2`, name)
data_fake <- df_ind_map %>% 
  select(code = "hc-a2") %>% 
  mutate(value = 1e6 * abs(rt(nrow(.), df = 10)))

glimpse(data_fake)
Rows: 34
Columns: 2
$ code  <chr> "PY", "LD", "WB", "OR", "BR", "SK", "CT", "TN", "MP", "GU", "GA", "NL", "MN", "AR", "...
$ value <dbl> 2593212.71, 143636.99, 93399.94, 327319.66, 554691.31, 824416.34, 830512.10, 2102316....
hcmap(
  "countries/in/in-all",
  data = data_fake,
  value = "value",
  joinBy = c("hc-a2", "code"),
  name = "Fake data",
  datalabels = list(enabled = TRUE, format = "{point.name}"),
  borderColor = "#FAFAFA",
  borderWidth = 0.1,
  tooltip = list(
    valueDecimals = 2,
    valuePrefix = "$")
)
trying URL 'https://code.highcharts.com/mapdata/countries/in/in-all.js'
Content type 'text/javascript' length 52340 bytes (51 KB)
downloaded 51 KB
intersect(df_ind$State.UnionTerritory, df_ind_map$name)
 [1] "Kerala"            "Delhi"             "Rajasthan"         "Uttar Pradesh"     "Haryana"          
 [6] "Tamil Nadu"        "Karnataka"         "Maharashtra"       "Punjab"            "Jammu and Kashmir"
[11] "Andhra Pradesh"    "Puducherry"        "West Bengal"       "Chhattisgarh"      "Chandigarh"       
[16] "Gujarat"           "Himachal Pradesh"  "Madhya Pradesh"    "Bihar"             "Manipur"          
[21] "Mizoram"           "Goa"               "Assam"             "Jharkhand"         "Arunachal Pradesh"
[26] "Tripura"           "Nagaland"          "Meghalaya"         "Sikkim"           
dim(df_ind)
[1] 9291   15
df_ind <- left_join(x = df_ind, y = (df_ind_map %>% select(name,`hc-a2`)),
          by = c("State.UnionTerritory" = "name"))

df_ind

Telangana, Ladakh couldn’t be find

df_ind[which(df_ind$State.UnionTerritory == "Daman & Diu"), "hc-a2"] <- "DA"
df_ind[which(df_ind$State.UnionTerritory == "Odisha"), "hc-a2"] <- "OR"
df_ind[which(df_ind$State.UnionTerritory == "Dadra and Nagar Haveli and Daman and Diu"), "hc-a2"] <- "DN"
df_ind[which(df_ind$State.UnionTerritory == "Uttarakhand"), "hc-a2"] <- "UT"
hcmap(
  "countries/in/in-all",
  data = (df_ind %>% filter(Date == max(Date, na.rm = TRUE))),
  value = "Confirmed",
  joinBy = c("hc-a2"),
  name = "Confirmed Cases",
  datalabels = list(enabled = TRUE, format = "{point.name}"),
  borderColor = "#FAFAFA",
  borderWidth = 0.1,
  tooltip = list(
    # valueDecimals = 2,
    # valuePrefix = "$"
    )
) %>% 
  hc_title(text = "Confirmed Cases Statewise across India")
trying URL 'https://code.highcharts.com/mapdata/countries/in/in-all.js'
Content type 'text/javascript' length 52340 bytes (51 KB)
downloaded 51 KB

1.5.13.1 Map Charts - ConfirmedPercap

hcmap(
  "countries/in/in-all",
  data = (df_ind %>% 
            filter(Date == max(Date, na.rm = TRUE)) %>% 
            mutate(CasesPercapPerc = Confirmed/Population_State * 100)),
  value = "CasesPercapPerc",
  joinBy = c("hc-a2"),
  name = "Confirmed Cases Per Capita (%)",
  datalabels = list(enabled = TRUE, format = "{point.name}"),
  borderColor = "#FAFAFA",
  borderWidth = 0.1,
  tooltip = list(
    valueDecimals = 2,
    valueSuffix = " %"
    )
) %>% 
  hc_title(text = "Confirmed Cases Per Capita(%) Statewise across India") %>% 
  hc_subtitle(text = "Delhi has highest Confirmed Cases Per Capita(%) India") %>% 
  hc_caption(text = "created by ViSa!!")
trying URL 'https://code.highcharts.com/mapdata/countries/in/in-all.js'
Content type 'text/javascript' length 52340 bytes (51 KB)
downloaded 51 KB

1.5.13.2 Map Charts color- ConfirmedPercap

hcmap(
  "countries/in/in-all",
  data = (df_ind %>% 
            filter(Date == max(Date, na.rm = TRUE)) %>% 
            mutate(CasesPercapPerc = Confirmed/Population_State * 100)),
  value = "CasesPercapPerc",
  joinBy = c("hc-a2"),
  name = "Confirmed Cases Per Capita (%)",
  datalabels = list(enabled = TRUE, format = "{point.name}"),
  borderColor = "#FAFAFA",
  borderWidth = 0.1,
  tooltip = list(
    valueDecimals = 2,
    valueSuffix = " %"
    )
) %>%
  hc_colorAxis(stops = color_stops()) %>% 
  hc_title(text = "Confirmed Cases Per Capita(%) Statewise across India") %>% 
  hc_subtitle(text = "Delhi has highest Confirmed Cases Per Capita(%) India") %>% 
  hc_caption(text = "created by ViSa!!")
trying URL 'https://code.highcharts.com/mapdata/countries/in/in-all.js'
Content type 'text/javascript' length 52340 bytes (51 KB)
downloaded 51 KB

1.5.13.3 Map Charts custom color- ConfirmedPercap

from: https://www.datanovia.com/en/lessons/highchart-interactive-world-map-in-r/

hcmap(
  "countries/in/in-all",
  data = (df_ind %>% 
            filter(Date == max(Date, na.rm = TRUE)) %>% 
            mutate(CasesPercapPerc = Confirmed/Population_State * 100)),
  value = "CasesPercapPerc",
  joinBy = c("hc-a2"),
  name = "Confirmed Cases Per Capita (%)",
  datalabels = list(enabled = TRUE, format = "{point.name}"),
  borderColor = "#FAFAFA",
  borderWidth = 0.1,
  tooltip = list(
    valueDecimals = 2,
    valueSuffix = " %"
    )
) %>%
  hc_colorAxis(minColor = "purple", maxColor = "red") %>% 
  hc_title(text = "Confirmed Cases Per Capita(%) Statewise across India") %>% 
  hc_subtitle(text = "Delhi has highest Confirmed Cases Per Capita(%) India") %>% 
  hc_caption(text = "created by ViSa!!")
trying URL 'https://code.highcharts.com/mapdata/countries/in/in-all.js'
Content type 'text/javascript' length 52340 bytes (51 KB)
downloaded 51 KB

1.5.13.4 Map Charts - CFR

hcmap(
  "countries/in/in-all",
  data = (df_ind %>% 
            filter(Date == max(Date, na.rm = TRUE)) 
            ),
  value = "CFR",
  joinBy = c("hc-a2"),
  name = "CFR(%)",
  datalabels = list(enabled = TRUE, format = "{point.name}"),
  borderColor = "#FAFAFA",
  borderWidth = 0.1,
  tooltip = list(
    valueDecimals = 2,
    valueSuffix = " %"
    )
) %>% 
  hc_title(text = "CFR(%) Statewise across India") %>% 
  hc_subtitle(text = "Punjab, Maharashtra has highest CFR(%) India") %>% 
  hc_caption(text = "created by ViSa!!")
trying URL 'https://code.highcharts.com/mapdata/countries/in/in-all.js'
Content type 'text/javascript' length 52340 bytes (51 KB)
downloaded 51 KB

1.5.13.5 Map Charts - CFR

hcmap(
  "countries/in/in-all",
  data = (df_ind %>% 
            filter(Date == max(Date, na.rm = TRUE)) 
            ),
  value = "CFR",
  joinBy = c("hc-a2"),
  name = "CFR(%)",
  datalabels = list(enabled = TRUE, format = "{point.name}"),
  borderColor = "#FAFAFA",
  borderWidth = 0.1,
  tooltip = list(
    valueDecimals = 2,
    valueSuffix = " %"
    )
) %>% 
  hc_colorAxis(stops = color_stops()) %>%
  hc_title(text = "CFR(%) Statewise across India") %>% 
  hc_subtitle(text = "Punjab, Maharashtra has highest CFR(%) India") %>% 
  hc_caption(text = "created by ViSa!!")
trying URL 'https://code.highcharts.com/mapdata/countries/in/in-all.js'
Content type 'text/javascript' length 52340 bytes (51 KB)
downloaded 51 KB
LS0tDQp0aXRsZTogIlRoaXMgaXMgYSB0ZW1wbGF0ZSINCm91dHB1dDogDQogIGh0bWxfbm90ZWJvb2s6DQogICAgaGlnaGxpZ2h0OiB0YW5nbw0KICAgIGRmX3ByaW50OiBwYWdlZA0KICAgIHRvYzogdHJ1ZQ0KICAgIHRvY19mbG9hdDogDQogICAgICBjb2xsYXBzZWQ6IGZhbHNlDQogICAgICBzbW9vdGhfc2Nyb2xsOiBmYWxzZQ0KICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQ0KICAgIHRvY19kZXB0aDogNg0KLS0tDQoNCg0KIyBEb2N1bWVudCBIZWFkZXINCg0KIyMgb3B0aW9ucyAmIHNldHRpbmdzDQoNCg0KY2h1bmsgb3B0aW9ucw0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0UsIGRwaSA9IDMwMCwgb3V0LndpZHRoID0gIjEwMCUiLGF0dHIub3V0cHV0PSdzdHlsZT0ibWF4LWhlaWdodDogMzAwcHg7IicpDQpgYGANCg0KDQpDU1MgZm9yIHNjcm9sbGFibGUgb3V0cHV0ICYgSGVhZGVyIGNvbG9ycyANCg0KYGBge2NzcywgZWNobz1GQUxTRX0NCi5zY3JvbGwtMTAwIHsNCiAgbWF4LWhlaWdodDogMTAwcHg7DQogIG92ZXJmbG93LXk6IGF1dG87DQogIGJhY2tncm91bmQtY29sb3I6IGluaGVyaXQ7DQp9DQoNCnRib2R5IHRyOmhvdmVyIHsNCiAgYmFja2dyb3VuZDogI2RkZGRkZDsNCn0NCg0KDQpoMSwgI1RPQz51bD5saSB7DQogIGNvbG9yOiAjQjY0RDNBOw0KfQ0KDQpoMiwgI1RPQz51bD51bD5saSB7DQogIGNvbG9yOiAjMDAwMDAwOw0KfQ0KDQpoMywgI1RPQz51bD51bD51bD5saSB7DQogIGNvbG9yOiAjNjQzY2IyOw0KfQ0KDQpoNCwgI1RPQz51bD51bD51bD51bD5saSB7DQogIGNvbG9yOiAjYWUwMDU4Ow0KfQ0KDQpoNSwgI1RPQz51bD51bD51bD51bD51bD5saSB7DQogIGNvbG9yOiAjZmZhNDQ3Ow0KfQ0KDQpoNiwgI1RPQz51bD51bD51bD51bD51bD51bD5saSB7DQogIGNvbG9yOiAjREFFM0Q5Ow0KfQ0KDQp0YWJsZSB7DQogIGJvcmRlci1jb2xsYXBzZTogY29sbGFwc2U7DQogIGJvcmRlci1ib3R0b206IDNweCBzb2xpZCAjZGRkZGRkOw0KICBib3JkZXItdG9wOiAzcHggc29saWQgI2RkZGRkZDsNCiAgYm9yZGVyLWxlZnQ6IDNweCBzb2xpZCAjZGRkZGRkOw0KICBib3JkZXItcmlnaHQ6IDNweCBzb2xpZCAjZGRkZGRkOw0KICAgYm94LXNoYWRvdzogMHB4IDBweCAyMHB4IHJnYmEoMCwwLDAsMC4wNSksDQogICAgIDBweCAxMHB4IDIwcHggcmdiYSgwLDAsMCwwLjA1KSwNCiAgICAgMHB4IDIwcHggMjBweCByZ2JhKDAsMCwwLDAuMDUpLA0KICAgICAwcHggMzBweCAyMHB4IHJnYmEoMCwwLDAsMC4wNSk7DQp9DQoNCmBgYA0KDQpUdXJuaW5nIHNjaWVudGlmaWMgLyBFeHBvbmVudGlhbCBudW1iZXJzIG9mZg0KDQpgYGB7cn0NCm9wdGlvbnMoc2NpcGVuID0gOTk5KQ0KYGBgDQoNCg0KIyMgU291cmNlDQoNCmRhdGEgZnJvbTogZnJvbTogaHR0cHM6Ly93d3cua2FnZ2xlLmNvbS9zdWRhbGFpcmFqa3VtYXIvY292aWQxOS1pbi1pbmRpYQ0KDQoNCiMjIExvYWRpbmcgbGlicw0KDQpgYGB7cn0NCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShnZ3RoZW1lcykNCmxpYnJhcnkobHVicmlkYXRlKQ0KbGlicmFyeShnZ2hpZ2hsaWdodCkNCmxpYnJhcnkoc3RyZWFtZ3JhcGgpDQpsaWJyYXJ5KHBsb3RseSkNCmxpYnJhcnkoaGlnaGNoYXJ0ZXIpDQpsaWJyYXJ5KGdsdWUpDQpsaWJyYXJ5KGdnaGFsdmVzKQ0KbGlicmFyeShodG1sd2lkZ2V0cykNCmxpYnJhcnkoUkNvbG9yQnJld2VyKQ0KbGlicmFyeSh2aXJpZGlzKQ0KbGlicmFyeShnZ3RleHQpDQpgYGANCg0KDQojIyBDcmVhdGluZyAmIHNldHRpbmcgY3VzdG9tIHRoZW1lDQoNCmBgYHtyfQ0KDQp0aGVtZV92aW55X2JyaWdodCA8LSBmdW5jdGlvbigpew0KICANCiAgbGlicmFyeShnZ3RoZW1lcykNCiAgDQogIGdndGhlbWVzOjp0aGVtZV9maXZldGhpcnR5ZWlnaHQoKSAlK3JlcGxhY2UlDQogIA0KICB0aGVtZSgNCiAgICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KCksDQogICAgDQogICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMyksDQogICAgDQogICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwNCiAgICANCiAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAid2hpdGUiKSwNCiAgICANCiAgICBwbG90LmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJ3aGl0ZSIpLA0KICAgIA0KICAgIHN0cmlwLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksDQogICAgDQogICAgbGVnZW5kLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9IE5BKSwNCiAgICANCiAgICBsZWdlbmQua2V5ID0gZWxlbWVudF9yZWN0KGZpbGwgPSBOQSksDQoNCiAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2l6ZSA9IDE5LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmFjZSA9ICJib2xkIiksDQogICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgY29sb3VyID0gIm1hcm9vbiIpDQogICAgICApDQogIA0KICB9DQoNCnRoZW1lX3NldCh0aGVtZV92aW55X2JyaWdodCgpKQ0KYGBgDQoNCg0KIyMgTG9hZGluZyBkYXRhDQoNCmBgYHtyfQ0KZGZfaW5kIDwtIHJlYWQuY3N2KCIuLi9kYXRhIGthZ2dsZS9jb3ZpZF8xOV9pbmRpYS5jc3YiKQ0KZGZfaW5kDQpgYGANCg0KDQpgYGB7cn0NCnN0cihkZl9pbmQpDQpgYGANCg0KYGBge3J9DQpzdW1tYXJ5KGRmX2luZCkNCmBgYA0KDQpgYGB7cn0NCmRmX2luZCA8LSBkZl9pbmQgJT4lIA0KICBtdXRhdGUoRGF0ZSA9IGx1YnJpZGF0ZTo6ZG15KERhdGUpLA0KICAgICAgICAgU3RhdGUuVW5pb25UZXJyaXRvcnkgPSBhcy5mYWN0b3IoU3RhdGUuVW5pb25UZXJyaXRvcnkpLA0KICAgICAgICAgIyBUaW1lID0gbHVicmlkYXRlOjpwYXJzZV9kYXRlX3RpbWUoVGltZSwgIkk6TSBwIiksDQogICAgICAgICBUaW1lID0gYXMuZmFjdG9yKFRpbWUpLA0KICAgICAgICAgQ29uZmlybWVkSW5kaWFuTmF0aW9uYWwgPSBhcy5pbnRlZ2VyKENvbmZpcm1lZEluZGlhbk5hdGlvbmFsKSwNCiAgICAgICAgIENvbmZpcm1lZEZvcmVpZ25OYXRpb25hbCA9IGFzLmludGVnZXIoQ29uZmlybWVkRm9yZWlnbk5hdGlvbmFsKQ0KICAgICAgICAgKQ0KDQpkZl9pbmQNCmBgYA0KDQoNCmBgYHtyfQ0Kc3VtbWFyeShkZl9pbmQpDQpgYGANCg0KDQpgYGB7cn0NCmRmX2luZCA8LSBkZl9pbmQgJT4lIA0KICBtdXRhdGUoQ0ZSID0gcm91bmQoRGVhdGhzIC8gQ29uZmlybWVkICogMTAwLCBkaWdpdHMgPSAyKSwNCiAgICAgICAgIFJlY292ZXJ5X1JhdGUgPSByb3VuZChDdXJlZCAvIENvbmZpcm1lZCAqIDEwMCwgZGlnaXRzID0gMikpDQoNCmRmX2luZA0KYGBgDQoNCg0KYGBge3J9DQpkZl9pbmQgPC0gZGZfaW5kICU+JSANCiAgZ3JvdXBfYnkoU3RhdGUuVW5pb25UZXJyaXRvcnkpICU+JSANCiAgbXV0YXRlKERhaWx5X2NvbmZpcm1lZCA9IENvbmZpcm1lZCAtIGxhZyhDb25maXJtZWQsIGRlZmF1bHQgPSAwKSwNCiAgICAgICAgIERhaWx5X2RlYXRocyA9IERlYXRocyAtIGxhZyhEZWF0aHMsIGRlZmF1bHQgPSAwKSwNCiAgICAgICAgIERhaWx5X2N1cmVkID0gQ3VyZWQgLSBsYWcoQ3VyZWQsIGRlZmF1bHQgPSAwKSkgJT4lIA0KICB1bmdyb3VwKCkNCg0KZGZfaW5kDQpgYGANCg0KYGBge3J9DQpkZl9pbmQkU3RhdGUuVW5pb25UZXJyaXRvcnkgJT4lIA0KICB0YWJsZSgpDQpgYGANCg0KYGBge3J9DQpkZl9pbmQgJT4lIA0KICBmaWx0ZXIoU3RhdGUuVW5pb25UZXJyaXRvcnkgJWluJSBjKCJDaGFuZGlnYXJoKioqIiwiTWFoYXJhc2h0cmEqKioiLCJUZWxhbmdhbmEqKioiLCJUZWxlbmdhbmEqKioiKSkNCmBgYA0KDQpUaGlzIGlzIHRoZSBjb2RlIHRvIHJlcGxhY2UgKioqIFN0YXRlcyB3aXRoIG9yaWdpbmFsIHdoaWNoIHdlIGFyZSBub3QgZ29ubmEgdXNlDQoNCmBgYHtyIGV2YWw9RkFMU0V9DQpkZl9pbmQgPC0gZGZfaW5kICU+JSANCiAgbXV0YXRlKFN0YXRlLlVuaW9uVGVycml0b3J5ID0gcmVwbGFjZShTdGF0ZS5VbmlvblRlcnJpdG9yeSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBTdGF0ZS5VbmlvblRlcnJpdG9yeSAlaW4lIGMoIlRlbGVuZ2FuYSIsIlRlbGVuZ2FuYSoqKiIsIlRlbGFuZ2FuYSoqKiIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJUZWxhbmdhbmEiKSwNCiAgICAgICAgIFN0YXRlLlVuaW9uVGVycml0b3J5ID0gcmVwbGFjZShTdGF0ZS5VbmlvblRlcnJpdG9yeSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBTdGF0ZS5VbmlvblRlcnJpdG9yeSAlaW4lICJNYWhhcmFzaHRyYSoqKiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk1haGFyYXNodHJhIiksDQogICAgICAgICBTdGF0ZS5VbmlvblRlcnJpdG9yeSA9IHJlcGxhY2UoU3RhdGUuVW5pb25UZXJyaXRvcnksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgU3RhdGUuVW5pb25UZXJyaXRvcnkgJWluJSAiQ2hhbmRpZ2FyaCoqKiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkNoYW5kaWdhcmgiKSwNCiAgICAgICAgIFN0YXRlLlVuaW9uVGVycml0b3J5ID0gcmVwbGFjZShTdGF0ZS5VbmlvblRlcnJpdG9yeSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBTdGF0ZS5VbmlvblRlcnJpdG9yeSAlaW4lICJQdW5qYWIqKioiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJQdW5qYWIiKSwNCiAgICAgICAgIA0KICAgICAgICAgIyB0byBnZXQgcmlkIG9mIG9sZCByZXBsYWNlZCBsZXZlbHMgZnJvbSBmYWN0b3IgY29sdW1ucw0KICAgICAgICAgU3RhdGUuVW5pb25UZXJyaXRvcnkgPSBhcy5jaGFyYWN0ZXIoU3RhdGUuVW5pb25UZXJyaXRvcnkpLA0KICAgICAgICAgU3RhdGUuVW5pb25UZXJyaXRvcnkgPSBhcy5mYWN0b3IoU3RhdGUuVW5pb25UZXJyaXRvcnkpDQogICkgDQoNCmRmX2luZA0KYGBgDQoNCg0KDQpgYGB7ciB9DQpkZl9pbmQgPC0gZGZfaW5kICU+JSANCiAgbXV0YXRlKFN0YXRlLlVuaW9uVGVycml0b3J5ID0gcmVwbGFjZShTdGF0ZS5VbmlvblRlcnJpdG9yeSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBTdGF0ZS5VbmlvblRlcnJpdG9yeSAlaW4lIGMoIlRlbGVuZ2FuYSIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJUZWxhbmdhbmEiKSwNCiAgICAgICAgIFN0YXRlLlVuaW9uVGVycml0b3J5ID0gcmVwbGFjZShTdGF0ZS5VbmlvblRlcnJpdG9yeSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBTdGF0ZS5VbmlvblRlcnJpdG9yeSAlaW4lIGMoIlRlbGVuZ2FuYSoqKiIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJUZWxhbmdhbmEqKioiKSwNCiAgICAgICAgIA0KICAgICAgICAgIyB0byBnZXQgcmlkIG9mIG9sZCByZXBsYWNlZCBsZXZlbHMgZnJvbSBmYWN0b3IgY29sdW1ucw0KICAgICAgICAgU3RhdGUuVW5pb25UZXJyaXRvcnkgPSBhcy5jaGFyYWN0ZXIoU3RhdGUuVW5pb25UZXJyaXRvcnkpLA0KICAgICAgICAgU3RhdGUuVW5pb25UZXJyaXRvcnkgPSBhcy5mYWN0b3IoU3RhdGUuVW5pb25UZXJyaXRvcnkpDQogICkgDQoNCmRmX2luZA0KYGBgDQoNCiMjIyBBZGRpbmcgcG9wdWxhdGlvbg0KDQpgYGB7cn0NCmRmX2luZCAlPiUgDQogIHB1bGwoU3RhdGUuVW5pb25UZXJyaXRvcnkpICU+JSANCiAgdW5pcXVlKCkNCmBgYA0KDQpmcm9tOiBodHRwczovL3VpZGFpLmdvdi5pbi9pbWFnZXMvc3RhdGUtd2lzZS1hYWRoYWFyLXNhdHVyYXRpb24ucGRmDQoNCmBgYHtyfQ0KZGZfaW5kIDwtIGRmX2luZCAlPiUgDQogIG11dGF0ZShQb3B1bGF0aW9uX1N0YXRlID0gY2FzZV93aGVuKFN0YXRlLlVuaW9uVGVycml0b3J5ID09ICJLZXJhbGEiIH4gMzU2OTk0NDMsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFN0YXRlLlVuaW9uVGVycml0b3J5ID09ICJUZWxhbmdhbmEiIH4gMzkzNjI3MzIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFN0YXRlLlVuaW9uVGVycml0b3J5ID09ICJEZWxoaSIgfiAxODcxMDkyMiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgU3RhdGUuVW5pb25UZXJyaXRvcnkgPT0gIlJhamFzdGhhbiIgfiA4MTAzMjY4OSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgU3RhdGUuVW5pb25UZXJyaXRvcnkgPT0gIlV0dGFyIFByYWRlc2giIH4gMjM3ODgyNzI1LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBTdGF0ZS5VbmlvblRlcnJpdG9yeSA9PSAiSGFyeWFuYSIgfiAyODIwNDY5MiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgU3RhdGUuVW5pb25UZXJyaXRvcnkgPT0gIkxhZGFraCIgfiAyODkwMjMsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFN0YXRlLlVuaW9uVGVycml0b3J5ID09ICJUYW1pbCBOYWR1IiB+IDc3ODQxMjY3LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBTdGF0ZS5VbmlvblRlcnJpdG9yeSA9PSAiS2FybmF0YWthIiB+IDY3NTYyNjg2LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBTdGF0ZS5VbmlvblRlcnJpdG9yeSA9PSAiTWFoYXJhc2h0cmEiIH4gMTIzMTQ0MjIzLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBTdGF0ZS5VbmlvblRlcnJpdG9yeSA9PSAiUHVuamFiIiB+IDMwMTQxMzczLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBTdGF0ZS5VbmlvblRlcnJpdG9yeSA9PSAiSmFtbXUgYW5kIEthc2htaXIiIH4gMTM2MDYzMjAsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFN0YXRlLlVuaW9uVGVycml0b3J5ID09ICJBbmRocmEgUHJhZGVzaCIgfiA1MzkwMzM5MywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgU3RhdGUuVW5pb25UZXJyaXRvcnkgPT0gIlV0dGFyYWtoYW5kIiB+IDExMjUwODU4LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBTdGF0ZS5VbmlvblRlcnJpdG9yeSA9PSAiT2Rpc2hhIiB+IDQ2MzU2MzM0LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBTdGF0ZS5VbmlvblRlcnJpdG9yeSA9PSAiUHVkdWNoZXJyeSIgfiAxNDEzNTQyLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBTdGF0ZS5VbmlvblRlcnJpdG9yeSA9PSAiV2VzdCBCZW5nYWwiIH4gOTk2MDkzMDMsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFN0YXRlLlVuaW9uVGVycml0b3J5ID09ICJDaGhhdHRpc2dhcmgiIH4gMjk0MzYyMzEsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFN0YXRlLlVuaW9uVGVycml0b3J5ID09ICJDaGFuZGlnYXJoIiB+IDExNTg0NzMsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFN0YXRlLlVuaW9uVGVycml0b3J5ID09ICJHdWphcmF0IiB+IDYzODcyMzk5LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBTdGF0ZS5VbmlvblRlcnJpdG9yeSA9PSAiSGltYWNoYWwgUHJhZGVzaCIgfiA3NDUxOTU1LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBTdGF0ZS5VbmlvblRlcnJpdG9yeSA9PSAiTWFkaHlhIFByYWRlc2giIH4gODUzNTg5NjUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFN0YXRlLlVuaW9uVGVycml0b3J5ID09ICJCaWhhciIgfiAxMjQ3OTk5MjYsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFN0YXRlLlVuaW9uVGVycml0b3J5ID09ICJNYW5pcHVyIiB+IDMwOTE1NDUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFN0YXRlLlVuaW9uVGVycml0b3J5ID09ICJNaXpvcmFtIiB+IDEyMzkyNDQsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFN0YXRlLlVuaW9uVGVycml0b3J5ID09ICJBbmRhbWFuIGFuZCBOaWNvYmFyIElzbGFuZHMgIiB+IDQxNzAzNiwgIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBTdGF0ZS5VbmlvblRlcnJpdG9yeSA9PSAiR29hIiB+IDE1ODYyNTAsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFN0YXRlLlVuaW9uVGVycml0b3J5ID09ICJBc3NhbSIgfiAzNTYwNzAzOSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgU3RhdGUuVW5pb25UZXJyaXRvcnkgPT0gIkpoYXJrYW5kIiB+IDM4NTkzOTQ4LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBTdGF0ZS5VbmlvblRlcnJpdG9yeSA9PSAiQXJ1bmFjaGFsIFByYWRlc2giIH4gMTU3MDQ1OCwgICAgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFN0YXRlLlVuaW9uVGVycml0b3J5ID09ICJUcmlwdXJhIiB+IDQxNjk3OTQsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFN0YXRlLlVuaW9uVGVycml0b3J5ID09ICJOYWdhbGFuZCIgfiAyMjQ5Njk1LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBTdGF0ZS5VbmlvblRlcnJpdG9yeSA9PSAiTWVnaGFsYXlhIiB+IDMzNjY3MTAsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFN0YXRlLlVuaW9uVGVycml0b3J5ID09ICJEYWRhciBOYWdhciBIYXZlbGkiIH4gNjE1NzI0LCAgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFN0YXRlLlVuaW9uVGVycml0b3J5ID09ICJTaWtraW0iIH4gNjkwMjUxLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBTdGF0ZS5VbmlvblRlcnJpdG9yeSA9PSAiRGFtYW4gJiBEaXUiIH4gIDYxNTcyNCkNCiAgICAgICAgIA0KICAgICAgICAgKQ0KDQpkZl9pbmQNCmBgYA0KDQojIyMjIENyZWF0aW5nIG11bHRpcGxlIGdhdGhlci9sb25nIGNvbHVtbnMNCg0KYGBge3J9DQpkZl9pbmQgJT4lIA0KICBzZWxlY3QoLSDDry4uU25vKSAlPiUgDQogIGdhdGhlcihrZXkgPSAiRGFpbHlfY2FzZXNfdHlwZSIsIHZhbHVlID0gIkNhc2VzX2NvdW50cyIsIA0KICAgICAgICAgLWMoIlN0YXRlLlVuaW9uVGVycml0b3J5IiwgIkRhdGUiLCAiVGltZSIsICJQb3B1bGF0aW9uX1N0YXRlIiwgDQogICAgICAgICAgICAiQ29uZmlybWVkSW5kaWFuTmF0aW9uYWwiLCAiQ29uZmlybWVkRm9yZWlnbk5hdGlvbmFsIiwgIkNGUiINCikpICU+JSANCiAgcHVsbChEYWlseV9jYXNlc190eXBlKSAlPiUgDQogIHRhYmxlKCkNCmBgYA0KDQpgYGB7cn0NCmRmX2luZF9zdGFja2VkX2RhaWx5IDwtIGRmX2luZCAlPiUgDQogIHNlbGVjdCgtIGMoIsOvLi5Tbm8iLCJDb25maXJtZWQiLCJDdXJlZCIsIkRlYXRocyIpKSAlPiUgDQogIA0KICBnYXRoZXIoa2V5ID0gIkRhaWx5X2Nhc2VzX3R5cGUiLCB2YWx1ZSA9ICJEYWlseV9jYXNlc19jb3VudHMiLCANCiAgICAgICAgIC1jKCJTdGF0ZS5VbmlvblRlcnJpdG9yeSIsICJEYXRlIiwgIlRpbWUiLCAiUG9wdWxhdGlvbl9TdGF0ZSIsIA0KICAgICAgICAgICAgIkNvbmZpcm1lZEluZGlhbk5hdGlvbmFsIiwgIkNvbmZpcm1lZEZvcmVpZ25OYXRpb25hbCIsICJDRlIiLCJSZWNvdmVyeV9SYXRlIg0KKSkgDQogIA0KZGZfaW5kX3N0YWNrZWRfZGFpbHkNCmBgYA0KDQoNCmBgYHtyfQ0KZGZfaW5kX3N0YWNrZWRfY3VtIDwtIGRmX2luZCAlPiUgDQogIHNlbGVjdCgtIGMoIsOvLi5Tbm8iLCJEYWlseV9jb25maXJtZWQiLCJEYWlseV9jdXJlZCIsIkRhaWx5X2RlYXRocyIpKSAlPiUgDQogIA0KICBnYXRoZXIoa2V5ID0gIkNhc2VzX3R5cGUiLCB2YWx1ZSA9ICJDYXNlc19jb3VudHMiLCANCiAgICAgICAgIC1jKCJTdGF0ZS5VbmlvblRlcnJpdG9yeSIsICJEYXRlIiwgIlRpbWUiLCAiUG9wdWxhdGlvbl9TdGF0ZSIsIA0KICAgICAgICAgICAgIkNvbmZpcm1lZEluZGlhbk5hdGlvbmFsIiwgIkNvbmZpcm1lZEZvcmVpZ25OYXRpb25hbCIsICJDRlIiLCJSZWNvdmVyeV9SYXRlIg0KKSkgDQogIA0KZGZfaW5kX3N0YWNrZWRfY3VtDQpgYGANCg0KYGBge3J9DQpkZl9pbmRfc3RhY2tlZF9yYXRlcyA8LSBkZl9pbmQgJT4lIA0KICBzZWxlY3QoLSBjKCLDry4uU25vIikgKSU+JSANCiAgDQogIGdhdGhlcihrZXkgPSAiUmF0ZV90eXBlIiwgdmFsdWUgPSAiUmF0ZV92YWx1ZSIsIA0KICAgICAgICAgLWMoIlN0YXRlLlVuaW9uVGVycml0b3J5IiwgIkRhdGUiLCAiVGltZSIsICJQb3B1bGF0aW9uX1N0YXRlIiwNCiAgICAgICAgICAgICJDb25maXJtZWRJbmRpYW5OYXRpb25hbCIsICJDb25maXJtZWRGb3JlaWduTmF0aW9uYWwiLCANCiAgICAgICAgICAgICJEYWlseV9jb25maXJtZWQiLCAiRGFpbHlfY3VyZWQiLCAiRGFpbHlfZGVhdGhzIiwNCiAgICAgICAgICAgICJDb25maXJtZWQiLCAiQ3VyZWQiLCAiRGVhdGhzIikpDQogIA0KZGZfaW5kX3N0YWNrZWRfcmF0ZXMNCmBgYA0KDQoNCiMjIyBDdXJyZW50IENvbmZpcm1lZCBDYXNlcyBieSBTdGF0ZXMNCg0KYGBge3J9DQpkZl9pbmQgJT4lIA0KICBmaWx0ZXIoRGF0ZSA9PSBtYXgoRGF0ZSwgbmEucm0gPSBUUlVFKSkgJT4lIA0KICANCiAgaGlnaGNoYXJ0ZXI6OmRhdGFfdG9faGllcmFyY2hpY2FsKA0KICAgIGdyb3VwX3ZhcnMgPSBjKFN0YXRlLlVuaW9uVGVycml0b3J5KSwNCiAgICBzaXplX3ZhciA9IENvbmZpcm1lZCwNCiAgICAjIGNvbG9ycyA9IGdldE9wdGlvbigiaGljaGFydGVyLmNvbG9yX3BhbGV0dGUiKQ0KKSAlPiUgDQogIA0KICBoY2hhcnQodHlwZSA9ICJ0cmVlbWFwIikNCmBgYA0KDQpgYGB7cn0NCmRmX2luZCAlPiUgDQogIGZpbHRlcihEYXRlID09IG1heChEYXRlLCBuYS5ybSA9IFRSVUUpKSAlPiUgDQogIA0KICBoY2hhcnQodHlwZSA9ICJ0cmVlbWFwIiwgDQogICAgICAgICBoY2Flcyh4ID0gU3RhdGUuVW5pb25UZXJyaXRvcnksIHZhbHVlID0gQ29uZmlybWVkLCBjb2xvciA9IENvbmZpcm1lZCkNCikgJT4lIA0KICBoY19jb2xvckF4aXMoc3RvcHMgPSBjb2xvcl9zdG9wcyhjb2xvcnMgPSB2aXJpZGlzOjppbmZlcm5vKDEwKSkpICU+JSANCiAgDQogIGhjX3RpdGxlKHRleHQgPSAiUmVsYXRpdmUgc2l6ZSBvZiBDdW11bGF0aXZlIENvbmZpcm1lZCBDYXNlcyBvZiBJbmRpYW4gU3RhdGUvVVQiLCBhbGlnbiA9ICJjZW50ZXIiKQ0KYGBgDQoNCg0KYGBge3IgZmlnLndpZHRoPTgsIGZpZy5oZWlnaHQ9OH0NCmRmX2luZCAlPiUNCiAgZmlsdGVyKERhdGUgPT0gbWF4KERhdGUsIG5hLnJtID0gVFJVRSkpICU+JSANCiAgbXV0YXRlKFN0YXRlLlVuaW9uVGVycml0b3J5ID0gZmN0X3Jlb3JkZXIoU3RhdGUuVW5pb25UZXJyaXRvcnksIENvbmZpcm1lZCkpICU+JSANCiAgDQogIGdncGxvdChhZXMoeCA9IENvbmZpcm1lZCwgeSA9IFN0YXRlLlVuaW9uVGVycml0b3J5LCBmaWxsID0gU3RhdGUuVW5pb25UZXJyaXRvcnkpKSArDQogIGdlb21fY29sKCkgKw0KICB0aGVtZV9jbGFzc2ljKCkgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsDQogICAgICAgIGF4aXMudGlja3MueSA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgYXhpcy5saW5lLnkgPSBlbGVtZW50X2JsYW5rKCkpICsNCiAgc2NhbGVfeF9jb250aW51b3VzKGxhYmVscyA9IHNjYWxlczo6Y29tbWFfZm9ybWF0KCkpICsNCiAgbGFicyh0aXRsZSA9IGdsdWUoIkN1bXVsYXRpdmUgQ29uZmlybWVkIENhc2VzIGFzIG9mIHttYXgoZGZfaW5kJERhdGUsIG5hLnJtID0gVFJVRSl9IiksDQogICAgICAgY2FwdGlvbiA9ICJDcmVhdGVkIGJ5IFZpU2EgISEiKQ0KYGBgDQoNCg0KIyMjIE92ZXIgdGhlIHRpbWUNCg0KIyMjIyBUb3AgU3RhdGVzIHdpdGggdG90YWwgQ29uZmlybWVkIGNhc2VzDQoNCmBgYHtyIGZpZy53aWR0aD04LCBmaWcuaGVpZ2h0PTh9DQpkZl9pbmQgJT4lIA0KICBmaWx0ZXIoIVN0YXRlLlVuaW9uVGVycml0b3J5ICVpbiUgDQogICAgICAgICAgIGMoIk1haGFyYXNodHJhKioqIiwiUHVuamFiKioqIiwiQ2hhbmRpZ2FyaCoqKiIsIlRlbGFuZ2FuYSoqKiIpKSAlPiUgDQogIGdncGxvdChhZXMoeCA9IERhdGUsIHkgPSBDb25maXJtZWQsIGNvbCA9IFN0YXRlLlVuaW9uVGVycml0b3J5KSkgKw0KICBnZW9tX2xpbmUoc2l6ZSA9IDEuMSkgKw0KICBnZ2hpZ2hsaWdodChDb25maXJtZWQgPiA1MDAwMDApICsNCiAgbGFicyh0aXRsZSA9ICJTdGF0ZXMgaGlnaGxpZ2h0ZWQgYWJvdmUgY29uZmlybWVkIGNhc2VzOiA1MDAsMDAwIiwNCiAgICAgICBzdWJ0aXRsZSA9ICJNYWhhcmFzaHRyYSB0b3BzIHRoZSBsaXN0IiwNCiAgICAgICBjYXB0aW9uID0gIkNyZWF0ZWQgYnkgVmlTYSAhISIpDQpgYGANCg0KDQpgYGB7ciBmaWcud2lkdGg9OCwgZmlnLmhlaWdodD04fQ0KZGZfaW5kICU+JSANCiAgZmlsdGVyKERhdGUgPiBhcy5EYXRlKCIyMDIwLTA0LTAxIikpICU+JSANCiAgDQogIHN0cmVhbWdyYXBoKGRhdGUgPSAiRGF0ZSIsIGtleSA9ICJTdGF0ZS5VbmlvblRlcnJpdG9yeSIsIHZhbHVlID0gIkNvbmZpcm1lZCIpICU+JSANCiAgc2dfZmlsbF9icmV3ZXIoIlB1T3IiKSAlPiUgDQogIHNnX3RpdGxlKCJDb25maXJtZWQgY2FzZXMgU3RhdGUgd2lzZSByZWxhdGl2ZSBncm93dGggc2luY2UgQXByIikNCmBgYA0KDQojIyMjIFRvcCBTdGF0ZXMgd2l0aCBEYWlseSBDb25maXJtZWQgY2FzZXMNCg0KYGBge3J9DQpkZl9pbmQgJT4lIA0KICBncm91cF9ieShTdGF0ZS5VbmlvblRlcnJpdG9yeSkgJT4lIA0KICBzdW1tYXJpc2UobWF4X2RhaWx5X0Nhc2UgPSBtYXgoRGFpbHlfY29uZmlybWVkKSkgJT4lIA0KICB1bmdyb3VwKCkNCmBgYA0KDQpgYGB7cn0NCmRmX2luZCAlPiUgDQogIGdyb3VwX2J5KFN0YXRlLlVuaW9uVGVycml0b3J5KSAlPiUgDQogIHN1bW1hcmlzZShtYXhfZGFpbHlfQ2FzZSA9IG1heChEYWlseV9jb25maXJtZWQpKSAlPiUgDQogIHVuZ3JvdXAoKSAlPiUgDQogIHN1bW1hcmlzZShtYXhfb2ZfbWF4ID0gbWF4KG1heF9kYWlseV9DYXNlKSkNCmBgYA0KDQoNCmBgYHtyIGZpZy53aWR0aD04LCBmaWcuaGVpZ2h0PTh9DQpkZl9pbmQgJT4lIA0KICBmaWx0ZXIoIVN0YXRlLlVuaW9uVGVycml0b3J5ICVpbiUgDQogICAgICAgICAgIGMoIk1haGFyYXNodHJhKioqIiwiUHVuamFiKioqIiwiQ2hhbmRpZ2FyaCoqKiIsIlRlbGFuZ2FuYSoqKiIpKSAlPiUgDQogIA0KICBnZ3Bsb3QoYWVzKHggPSBEYXRlLCB5ID0gRGFpbHlfY29uZmlybWVkLCBjb2wgPSBTdGF0ZS5VbmlvblRlcnJpdG9yeSkpICsNCiAgZ2VvbV9saW5lKHNpemUgPSAuOCkgKw0KICBnZ2hpZ2hsaWdodChEYWlseV9jb25maXJtZWQgPiA0MDAwKSArDQogIGxhYnModGl0bGUgPSAiSGlnaGxpZ2h0ZWQgU3RhdGVzIHdpdGggZGFpbHkgY29uZmlybWVkIGNhc2VzID4gNDAwMCIsDQogICAgICAgY2FwdGlvbiA9ICJDcmVhdGVkIGJ5IFZpU2EgISEiKQ0KYGBgDQoNCg0KYGBge3IgZmlnLndpZHRoPTgsIGZpZy5oZWlnaHQ9OH0NCmRmX2luZCAlPiUgDQogIGZpbHRlcighU3RhdGUuVW5pb25UZXJyaXRvcnkgJWluJSANCiAgICAgICAgICAgYygiTWFoYXJhc2h0cmEqKioiLCJQdW5qYWIqKioiLCJDaGFuZGlnYXJoKioqIiwiVGVsYW5nYW5hKioqIikpICU+JQ0KICANCiAgZ2dwbG90KGFlcyh4ID0gRGF0ZSwgeSA9IERhaWx5X2NvbmZpcm1lZCwgY29sID0gU3RhdGUuVW5pb25UZXJyaXRvcnkpKSArDQogIGdlb21fbGluZSgpICsNCiAgc2NhbGVfeV9sb2cxMCgpICsNCiAgbGFicyh0aXRsZSA9ICJTdGF0ZXMgaGlnaGxpZ2h0ZWQgd2l0aCBEYWlseSBDb25maXJtZWQgY2FzZXMgPiA1MDAwIiwNCiAgICAgICBzdWJ0aXRsZSA9ICJJbiBsb2cgc2NhbGUiLA0KICAgICAgIGNhcHRpb24gPSAiQ3JlYXRlZCBieSBWaVNhICEhIiwNCiAgICAgICB5ID0gImxvZyBvZiBEYWlseSBDb25maXJtZWQiKQ0KICBnZ2hpZ2hsaWdodChEYWlseV9jb25maXJtZWQgPiA1MDAwKQ0KYGBgDQoNCg0KYGBge3IgZmlnLndpZHRoPTgsIGZpZy5oZWlnaHQ9OH0NCmRmX2luZCAlPiUgDQogIGZpbHRlcighU3RhdGUuVW5pb25UZXJyaXRvcnkgJWluJSANCiAgICAgICAgICAgYygiTWFoYXJhc2h0cmEqKioiLCJQdW5qYWIqKioiLCJDaGFuZGlnYXJoKioqIiwiVGVsYW5nYW5hKioqIikpICU+JSANCiAgZmlsdGVyKFN0YXRlLlVuaW9uVGVycml0b3J5ICE9ICJUZWxhbmdhbmEiKSAlPiUgDQogIA0KICBnZ3Bsb3QoYWVzKHggPSBEYXRlLCB5ID0gRGFpbHlfY29uZmlybWVkLCBjb2wgPSBTdGF0ZS5VbmlvblRlcnJpdG9yeSkpICsNCiAgZ2VvbV9saW5lKHNpemUgPSAuOCkgKw0KICBnZ2hpZ2hsaWdodChEYWlseV9jb25maXJtZWQgPiA0MDAwKSArDQogIGxhYnModGl0bGUgPSAiU3RhdGVzIGhpZ2hsaWdodGVkICgtIFRlbGFuZ2FuYSkiLA0KICAgICAgIHN1YnRpdGxlID0gImRhaWx5IGNvbmZpcm1lZCA+IDQwMDAiLA0KICAgICAgIGNhcHRpb24gPSAiQ3JlYXRlZCBieSBWaVNhICEhIikNCmBgYA0KDQoNCiMjIyMgRGFpbHkgQ29uZmlybWVkDQoNCmBgYHtyIGZpZy53aWR0aD04LCBmaWcuaGVpZ2h0PTh9DQpkZl9pbmQgJT4lIA0KICBmaWx0ZXIoIVN0YXRlLlVuaW9uVGVycml0b3J5ICVpbiUgDQogICAgICAgICAgIGMoIk1haGFyYXNodHJhKioqIiwiUHVuamFiKioqIiwiQ2hhbmRpZ2FyaCoqKiIsIlRlbGFuZ2FuYSoqKiIpKSAlPiUNCiAgZmlsdGVyKFN0YXRlLlVuaW9uVGVycml0b3J5ICVpbiUgKGRmX2luZCAlPiUgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbHRlcihEYXRlID09IG1heChEYXRlLCBuYS5ybSA9IFRSVUUpKSAlPiUgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRvcF9uKG4gPSAxMCwgd3QgPSBDb25maXJtZWQpICU+JSANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHVsbChTdGF0ZS5VbmlvblRlcnJpdG9yeSkpDQogICAgICAgICApICU+JSANCiAgbXV0YXRlKFN0YXRlLlVuaW9uVGVycml0b3J5ID0gZmN0X3Jlb3JkZXIoU3RhdGUuVW5pb25UZXJyaXRvcnksIERhaWx5X2NvbmZpcm1lZCkpICU+JSANCiAgDQogIGdncGxvdChhZXMoeCA9IFN0YXRlLlVuaW9uVGVycml0b3J5LCB5ID0gRGFpbHlfY29uZmlybWVkLCANCiAgICAgICAgICAgICBmaWxsID0gU3RhdGUuVW5pb25UZXJyaXRvcnksIGNvbCA9IFN0YXRlLlVuaW9uVGVycml0b3J5KSkgKw0KICBnZW9tX2hhbGZfYm94cGxvdChzaWRlID0gImwiLCBhbHBoYSA9IDAuNykgKw0KICBnZW9tX2hhbGZfdmlvbGluKHNpZGUgPSAiciIsIGFscGhhID0gMC43KSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTApLA0KICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsNCiAgbGFicyh0aXRsZSA9ICJEYWlseSBDb25maXJtZWQgQ2FzZXMgRGlzdHJpYnV0aW9uIG9mIHRvcCAxMCBTdGF0ZXMiLA0KICAgICAgIHN1YnRpdGxlID0gIk1haGFyYXNodHJhIHdpdGggaGlnaGVzdCBtZWRpYW4gYW5kIHBvaW50IHZhbHVlcyIsDQogICAgICAgY2FwdGlvbiA9ICJjcmVhdGVkIGJ5IFZpU2EgISEiKQ0KDQpgYGANCg0KIyMjIyBEYWlseSBDb25maXJtZWQNCg0KYGBge3IgZmlnLndpZHRoPTgsIGZpZy5oZWlnaHQ9OH0NCmRmX2luZCAlPiUgDQogIGZpbHRlcighU3RhdGUuVW5pb25UZXJyaXRvcnkgJWluJSANCiAgICAgICAgICAgYygiTWFoYXJhc2h0cmEqKioiLCJQdW5qYWIqKioiLCJDaGFuZGlnYXJoKioqIiwiVGVsYW5nYW5hKioqIikpICU+JQ0KICBmaWx0ZXIoU3RhdGUuVW5pb25UZXJyaXRvcnkgJWluJSAoZGZfaW5kICU+JSANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsdGVyKERhdGUgPT0gbWF4KERhdGUsIG5hLnJtID0gVFJVRSkpICU+JSANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdG9wX24obiA9IDEwLCB3dCA9IERlYXRocykgJT4lIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwdWxsKFN0YXRlLlVuaW9uVGVycml0b3J5KSkNCiAgICAgICAgICkgJT4lIA0KICBtdXRhdGUoU3RhdGUuVW5pb25UZXJyaXRvcnkgPSBmY3RfcmVvcmRlcihTdGF0ZS5VbmlvblRlcnJpdG9yeSwgRGFpbHlfZGVhdGhzKSkgJT4lIA0KICANCiAgZ2dwbG90KGFlcyh4ID0gU3RhdGUuVW5pb25UZXJyaXRvcnksIHkgPSBEYWlseV9kZWF0aHMsIA0KICAgICAgICAgICAgIGZpbGwgPSBTdGF0ZS5VbmlvblRlcnJpdG9yeSwgY29sID0gU3RhdGUuVW5pb25UZXJyaXRvcnkpKSArDQogIGdlb21faGFsZl9ib3hwbG90KHNpZGUgPSAibCIsIGFscGhhID0gMC43KSArDQogIGdlb21faGFsZl9wb2ludChzaWRlID0gInIiKSArDQogICMgZ2VvbV9qaXR0ZXIoYWxwaGEgPSAwLjcpICsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCksDQogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKw0KICBsYWJzKHRpdGxlID0gIkRhaWx5IERlYXRocyBEaXN0cmlidXRpb24gb2YgdG9wIDEwIFN0YXRlcyIsDQogICAgICAgc3VidGl0bGUgPSAiTWFoYXJhc2h0cmEgd2l0aCBoaWdoZXN0IG1lZGlhbiBhbmQgcG9pbnQgdmFsdWVzIiwNCiAgICAgICBjYXB0aW9uID0gImNyZWF0ZWQgYnkgVmlTYSAhISIpDQpgYGANCg0KDQpDRlINCg0KYGBge3IgZmlnLndpZHRoPTgsIGZpZy5oZWlnaHQ9OH0NCmRmX2luZCAlPiUgDQogIGZpbHRlcighU3RhdGUuVW5pb25UZXJyaXRvcnkgJWluJSANCiAgICAgICAgICAgYygiTWFoYXJhc2h0cmEqKioiLCJQdW5qYWIqKioiLCJDaGFuZGlnYXJoKioqIiwiVGVsYW5nYW5hKioqIikpICU+JQ0KICANCiAgZ2dwbG90KGFlcyh4ID0gRGF0ZSwgeSA9IENGUiwgY29sID0gU3RhdGUuVW5pb25UZXJyaXRvcnkpKSArDQogIGdlb21fbGluZSgpICsNCiAgbGFicyh0aXRsZSA9ICJDRlIgb2YgSW5kaWFuIFN0YXRlcyBmcm9tIGJlaWdubmluZyIsDQogICAgICAgeSA9ICJDRlIgKCUpIikNCmBgYA0KDQpgYGB7ciBmaWcud2lkdGg9OCwgZmlnLmhlaWdodD04fQ0KZGZfaW5kICU+JSANCiAgZmlsdGVyKCFTdGF0ZS5VbmlvblRlcnJpdG9yeSAlaW4lIA0KICAgICAgICAgICBjKCJNYWhhcmFzaHRyYSoqKiIsIlB1bmphYioqKiIsIkNoYW5kaWdhcmgqKioiLCJUZWxhbmdhbmEqKioiKSkgJT4lDQogIGZpbHRlcihEYXRlID4geW1kKCIyMDIwLTA1LTMxIikpICU+JSANCiAgDQogIGdncGxvdChhZXMoeCA9IERhdGUsIHkgPSBDRlIsIGNvbCA9IFN0YXRlLlVuaW9uVGVycml0b3J5KSkgKw0KICBnZW9tX2xpbmUoKSArDQogIGxhYnModGl0bGUgPSAiQ0ZSIG9mIEluZGlhbiBTdGF0ZXMvVVQiLA0KICAgICAgIHN1YnRpdGxlID0gIkp1bmUgb253YXJkcyIsIA0KICAgICAgIHkgPSAiQ0ZSICglKSIsDQogICAgICAgY2FwdGlvbiA9ICJjcmVhdGVkIGJ5IFZpU2EgISEiKQ0KYGBgDQoNCmBgYHtyIGZpZy53aWR0aD04LCBmaWcuaGVpZ2h0PTh9DQpkZl9pbmQgJT4lIA0KICBmaWx0ZXIoIVN0YXRlLlVuaW9uVGVycml0b3J5ICVpbiUgDQogICAgICAgICAgIGMoIk1haGFyYXNodHJhKioqIiwiUHVuamFiKioqIiwiQ2hhbmRpZ2FyaCoqKiIsIlRlbGFuZ2FuYSoqKiIpKSAlPiUNCiAgZmlsdGVyKERhdGUgPiBhcy5EYXRlKCIyMDIwLTA1LTMxIikpICU+JSANCiAgDQogIGdncGxvdChhZXMoeCA9IERhdGUsIHkgPSBDRlIsIGNvbCA9IFN0YXRlLlVuaW9uVGVycml0b3J5KSkgKw0KICBnZW9tX2xpbmUoc2l6ZSA9IDAuOCkgKw0KICBzY2FsZV9jb2xvcl90YWJsZWF1KCkgKw0KICBnZ2hpZ2hsaWdodChDRlIgPiAzKSArDQogIGxhYnModGl0bGUgPSAiQ0ZSIG9mIFN0YXRlcy9VVCB3aXRoIENGUiA+IDMlIiwNCiAgICAgICBzdWJ0aXRsZSA9ICIoSnVuZSBvbndhcmRzKSIsDQogICAgICAgeSA9ICJDRlIgKCUpIiwNCiAgICAgICBjYXB0aW9uID0gImNyZWF0ZWQgYnkgVmlTYSAhISIpDQpgYGANCg0KDQpgYGB7ciBmaWcud2lkdGg9OCwgZmlnLmhlaWdodD04fQ0KZGZfaW5kICU+JSANCiAgZmlsdGVyKCFTdGF0ZS5VbmlvblRlcnJpdG9yeSAlaW4lIA0KICAgICAgICAgICBjKCJNYWhhcmFzaHRyYSoqKiIsIlB1bmphYioqKiIsIkNoYW5kaWdhcmgqKioiLCJUZWxhbmdhbmEqKioiKSkgJT4lDQogIGZpbHRlcihEYXRlID4gYXMuRGF0ZSgiMjAyMC0wNS0zMSIpKSAlPiUgDQogIA0KICBnZ3Bsb3QoYWVzKHggPSBEYXRlLCB5ID0gQ0ZSLCBjb2wgPSBTdGF0ZS5VbmlvblRlcnJpdG9yeSkpICsNCiAgZ2VvbV9saW5lKHNpemUgPSAwLjgpICsNCiAgc2NhbGVfY29sb3JfdGFibGVhdSgpICsNCiAgZ2doaWdobGlnaHQoU3RhdGUuVW5pb25UZXJyaXRvcnkgJWluJSBjKCJEZWxoaSIsIk1haGFyYXNodHJhIiwiS2FybmF0a2EiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlV0dGFyIFByYWRlc2giLCJIYXJ5YW5hIiwiV2VzdCBCZW5nYWwiKSkgKw0KICBsYWJzKHRpdGxlID0gIkNGUiBvZiBTdGF0ZXMgSnVuZSBvbndhcmRzIiwNCiAgICAgICBzdWJ0aXRsZSA9ICJIaWdobGlnaHRlZCBmb3Igc2VsZWN0ZWQgU3RhdGVzIiwNCiAgICAgICB5ID0gIkNGUiAoJSkiLA0KICAgICAgIGNhcHRpb24gPSAiY3JlYXRlZCBieSBWaVNhICEhIikNCmBgYA0KDQoNCmBgYHtyIGZpZy53aWR0aD04LCBmaWcuaGVpZ2h0PTh9DQpkZl9pbmQgJT4lIA0KICBmaWx0ZXIoIVN0YXRlLlVuaW9uVGVycml0b3J5ICVpbiUgDQogICAgICAgICAgIGMoIk1haGFyYXNodHJhKioqIiwiUHVuamFiKioqIiwiQ2hhbmRpZ2FyaCoqKiIsIlRlbGFuZ2FuYSoqKiIpKSAlPiUNCiAgZmlsdGVyKERhdGUgPiBhcy5EYXRlKCIyMDIwLTA1LTMxIikpICU+JSANCiAgDQogIGdncGxvdChhZXMoeCA9IERhdGUsIHkgPSBDRlIsIGNvbCA9IFN0YXRlLlVuaW9uVGVycml0b3J5KSkgKw0KICBnZW9tX2xpbmUoc2l6ZSA9IDAuOCkgKw0KICBzY2FsZV9jb2xvcl90YWJsZWF1KCkgKw0KICBnZ2hpZ2hsaWdodChTdGF0ZS5VbmlvblRlcnJpdG9yeSAlaW4lIA0KICAgICAgICAgICAgICAgIChkZl9pbmQgJT4lIA0KICAgICAgICAgICAgICAgIGZpbHRlcihEYXRlID09IG1heChEYXRlLCBuYS5ybSA9IFRSVUUpKSAlPiUgDQogICAgICAgICAgICAgICAgdG9wX24obiA9IDcsIHd0ID0gQ0ZSKSAlPiUNCiAgICAgICAgICAgICAgICBwdWxsKFN0YXRlLlVuaW9uVGVycml0b3J5KSAgKQ0KICAgICAgICAgICAgICAgICkgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICBsYWJzKHRpdGxlID0gZ2x1ZSgiSGlnaGxpZ2h0ZWQgVG9wIDcgU3RhdGVzL1VUIHdpdGggaGlnaGVzdCBDRlIgYXMgb2Yge21heChkZl9pbmQkRGF0ZSwgbmEucm09VFJVRSl9IiksDQogICAgICAgc3VidGl0bGUgPSAiQ0ZSIG9mIFN0YXRlcyBKdW5lIG9ud2FyZHMiLA0KICAgICAgIHkgPSAiQ0ZSICglKSIsDQogICAgICAgY2FwdGlvbiA9ICJDcmVhdGVkIGJ5IFZpU2EgISEiKQ0KYGBgDQoNCmBgYHtyfQ0KZ2dzYXZlKGZpbGVuYW1lID0gIlRvcDdfQ0ZSX0luZGlhbl9TdGF0ZXMuanBnIiwgZHBpID0gMzAwLCBoZWlnaHQgPSA4LCB3aWR0aCA9IDEwKQ0KYGBgDQoNCg0KDQpgYGB7ciBmaWcud2lkdGg9OCwgZmlnLmhlaWdodD04fQ0Kc3RyZWFtX0NGUiA8LSBkZl9pbmQgJT4lIA0KICBmaWx0ZXIoIVN0YXRlLlVuaW9uVGVycml0b3J5ICVpbiUgDQogICAgICAgICAgIGMoIk1haGFyYXNodHJhKioqIiwiUHVuamFiKioqIiwiQ2hhbmRpZ2FyaCoqKiIsIlRlbGFuZ2FuYSoqKiIpKSAlPiUNCiAgDQogIHN0cmVhbWdyYXBoKGRhdGUgPSAiRGF0ZSIsIGtleSA9ICJTdGF0ZS5VbmlvblRlcnJpdG9yeSIsIHZhbHVlID0gIkNGUiIpICU+JSANCiAgc2dfZmlsbF90YWJsZWF1KCkgJT4lIA0KICAjIHNnX2ZpbGxfYnJld2VyKCJCdVB1IikgJT4lIA0KICBzZ190aXRsZSgiQ2FzZSBGYXRhbGl0eSBSYXRlIG9mIGFsbCBJbmRpYW4gU3RhdGVzIGFjcm9zcyAyMDIwIikNCg0Kc3RyZWFtX0NGUg0KYGBgDQoNCmBgYHtyIGZpZy53aWR0aD04LCBmaWcuaGVpZ2h0PTh9DQpzdHJlYW1fY29uZmlybWVkIDwtIGRmX2luZCAlPiUgDQogIGZpbHRlcighU3RhdGUuVW5pb25UZXJyaXRvcnkgJWluJSANCiAgICAgICAgICAgYygiTWFoYXJhc2h0cmEqKioiLCJQdW5qYWIqKioiLCJDaGFuZGlnYXJoKioqIiwiVGVsYW5nYW5hKioqIikpICU+JSANCiAgDQogIHN0cmVhbWdyYXBoKGRhdGUgPSAiRGF0ZSIsIGtleSA9ICJTdGF0ZS5VbmlvblRlcnJpdG9yeSIsIHZhbHVlID0gIkRhaWx5X2NvbmZpcm1lZCIpICU+JSANCiAgc2dfZmlsbF90YWJsZWF1KCkgJT4lIA0KICBzZ190aXRsZSh0aXRsZSA9ICJGbG93IG9mIERhaWx5IGNvbmZpcm1lZCBjYXNlcyBieSBhbGwgSW5kaWFuIFN0YXRlcyBhY3Jvc3MgMjAyMCIpDQoNCnN0cmVhbV9jb25maXJtZWQNCmBgYA0KDQpBZGRpbmcgYW5ub3RhdGlvbnMsIGxlZ2VuZCBmcm9tOiBodHRwczovL2Jsb2dzLndvcmxkYmFuay5vcmcvb3BlbmRhdGEvaW50ZXJhY3RpdmUtcHJvZHVjdC1leHBvcnQtc3RyZWFtZ3JhcGhzLWRhdGEzNjByLW5vdy1jcmFuDQoNCmBgYHtyIGZpZy53aWR0aD04LCBmaWcuaGVpZ2h0PTh9DQpzdHJlYW1fZGVhdGhzIDwtIGRmX2luZCAlPiUgDQogIGZpbHRlcighU3RhdGUuVW5pb25UZXJyaXRvcnkgJWluJSANCiAgICAgICAgICAgYygiTWFoYXJhc2h0cmEqKioiLCJQdW5qYWIqKioiLCJDaGFuZGlnYXJoKioqIiwiVGVsYW5nYW5hKioqIikpICU+JSANCiAgDQogIHN0cmVhbWdyYXBoKGRhdGUgPSAiRGF0ZSIsIGtleSA9ICJTdGF0ZS5VbmlvblRlcnJpdG9yeSIsIHZhbHVlID0gIkRhaWx5X2RlYXRocyIpICU+JSANCiAgc2dfZmlsbF90YWJsZWF1KCkgJT4lIA0KICBzZ190aXRsZSh0aXRsZSA9ICJGbG93IG9mIERhaWx5IGNvdmlkIGRlYXRocyByZXBvcnRlZCBieSBhbGwgSW5kaWFuIFN0YXRlcyBhY3Jvc3MgMjAyMCIpIyU+JQ0KICAjIHNnX2Fubm90YXRlKGxhYmVsPSJNYWhhcmFzaHRyYSIsIHg9YXMuRGF0ZSgiMjAyMC0wOS0yMCIpLCB5PTEwMDAsIGNvbG9yPSIjZmZmZmZmIiwgc2l6ZT0xOCkgIyU+JSANCiAgIyBzZ19sZWdlbmQoc2hvdz1UUlVFLCBsYWJlbD0gIlN0YXRlOiAiKSU+JQ0KDQpzdHJlYW1fZGVhdGhzDQpgYGANCg0KTm90IHNhdmluZyBzaGlueS50YWcgb2JqZWN0DQoNCmBgYHtyIGV2YWw9RkFMU0V9DQpodG1sd2lkZ2V0czo6c2F2ZVdpZGdldChzdHJlYW1fQ0ZSLCAic3RyZWFtX0NGUl9zdGF0ZXMuaHRtbCIpDQpgYGANCg0KYGBge3J9DQpjbGFzcyhzdHJlYW1fQ0ZSKQ0KYGBgDQoNCg0KDQojIyMgdG9wIDUgU3RhdGVzIG9mIGNvbmZpcm1lZCBjYXNlcw0KDQpgYGB7cn0NCmRmX2luZF9zdGFja2VkX2N1bSAlPiUgDQogIG11dGF0ZShtb250aCA9IGx1YnJpZGF0ZTo6bW9udGgoRGF0ZSwgbGFiZWwgPSBUUlVFLCBhYmJyID0gVFJVRSkpICU+JSANCiAgIyBmaWx0ZXIoQ2FzZXNfdHlwZSA9PSAiQ29uZmlybWVkIikgJT4lIA0KICBncm91cF9ieShTdGF0ZS5VbmlvblRlcnJpdG9yeSwgQ2FzZXNfdHlwZSwgbW9udGgpICU+JSANCiAgc3VtbWFyaXNlKENhc2VzX2NvdW50cyA9IG1heChDYXNlc19jb3VudHMsIG5hLnJtID0gVFJVRSkpDQpgYGANCg0KIyMjIFRvcCA3IGNvbmZpcm1lZCBjYXNlcyBTdGF0ZXMNCg0KYGBge3IgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTEwfQ0KZGZfaW5kX3N0YWNrZWRfY3VtICU+JSANCiAgZmlsdGVyKCFTdGF0ZS5VbmlvblRlcnJpdG9yeSAlaW4lIA0KICAgICAgICAgICBjKCJNYWhhcmFzaHRyYSoqKiIsIlB1bmphYioqKiIsIkNoYW5kaWdhcmgqKioiLCJUZWxhbmdhbmEqKioiLCAiQ2FzZXMgYmVpbmcgcmVhc3NpZ25lZCB0byBzdGF0ZXMiKSkgJT4lDQogIG11dGF0ZShtb250aCA9IGx1YnJpZGF0ZTo6bW9udGgoRGF0ZSwgbGFiZWwgPSBUUlVFLCBhYmJyID0gVFJVRSkpICU+JSANCiAgZmlsdGVyKENhc2VzX3R5cGUgPT0gIkNvbmZpcm1lZCIpICU+JQ0KICBncm91cF9ieShTdGF0ZS5VbmlvblRlcnJpdG9yeSwgbW9udGgpICU+JSANCiAgc3VtbWFyaXNlKENhc2VzX2NvdW50cyA9IG1heChDYXNlc19jb3VudHMsIG5hLnJtID0gVFJVRSkpICU+JSANCiAgZ3JvdXBfYnkobW9udGgpICU+JSANCiAgdG9wX24obiA9IDcsIHd0ID0gQ2FzZXNfY291bnRzKSAlPiUgDQogIHVuZ3JvdXAoKSAlPiUgDQogIG11dGF0ZShTdGF0ZS5VbmlvblRlcnJpdG9yeSA9IGZjdF9yZW9yZGVyKFN0YXRlLlVuaW9uVGVycml0b3J5LCBDYXNlc19jb3VudHMsIG1heCkpICU+JSANCg0KZ2dwbG90KGFlcyh4ID0gQ2FzZXNfY291bnRzLCB5ID0gU3RhdGUuVW5pb25UZXJyaXRvcnksIA0KICAgICAgICAgICBmaWxsID0gU3RhdGUuVW5pb25UZXJyaXRvcnksIGdyb3VwID0gU3RhdGUuVW5pb25UZXJyaXRvcnkgKSkgKw0KICBnZW9tX2NvbCgpICsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCksDQogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKw0KICBmYWNldF93cmFwKH5tb250aCkgKw0KICAjIGNvb3JkX2NhcnRlc2lhbihjbGlwID0gIm9mZiIpICsNCiAgc2NhbGVfZmlsbF90YWJsZWF1KHBhbGV0dGUgPSAiVGFibGVhdSAyMCIpICsNCiAgbGFicyh0aXRsZSA9ICJDb25maXJtZWQgY2FzZXM6IFRvcCA3IFN0YXRlcy9VVCBlYWNoIG1vbnRoIiwNCiAgICAgICBjYXB0aW9uID0gIkNyZWF0ZWQgYnkgVmlTYSAhISIpICsNCiAgc2NhbGVfeF9jb250aW51b3VzKGxhYmVsID0gc2NhbGVzOjpjb21tYV9mb3JtYXQoKSkNCmBgYA0KDQoNCiMjIyBUb3AgNyBDb25maXJtZWRwZXJDYXBpdGEgU3RhdGVzIE1heSBvbndhcmRzDQoNCmBgYHtyIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD0xMCwgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KZGZfaW5kX3N0YWNrZWRfY3VtICU+JSANCiAgZmlsdGVyKCFTdGF0ZS5VbmlvblRlcnJpdG9yeSAlaW4lIA0KICAgICAgICAgICBjKCJNYWhhcmFzaHRyYSoqKiIsIlB1bmphYioqKiIsIkNoYW5kaWdhcmgqKioiLCJUZWxhbmdhbmEqKioiLCAiQ2FzZXMgYmVpbmcgcmVhc3NpZ25lZCB0byBzdGF0ZXMiKSwNCiAgICAgICAgIERhdGUgPiBhcy5EYXRlKCIyMDIwLTA0LTMwIikNCiAgICAgICAgICkgJT4lDQogIG11dGF0ZShtb250aCA9IGx1YnJpZGF0ZTo6bW9udGgoRGF0ZSwgbGFiZWwgPSBUUlVFLCBhYmJyID0gVFJVRSkpICU+JSANCiAgZmlsdGVyKENhc2VzX3R5cGUgPT0gIkNvbmZpcm1lZCIpICU+JQ0KICBtdXRhdGUoQ29uZlBlcmNhcGl0YSA9IENhc2VzX2NvdW50cy9Qb3B1bGF0aW9uX1N0YXRlKSAlPiUgDQogIA0KICBncm91cF9ieShTdGF0ZS5VbmlvblRlcnJpdG9yeSwgbW9udGgpICU+JSANCiAgc3VtbWFyaXNlKENvbmZQZXJjYXBpdGEgPSBtYXgoQ29uZlBlcmNhcGl0YSwgbmEucm0gPSBUUlVFKSkgJT4lIA0KICBncm91cF9ieShtb250aCkgJT4lIA0KICB0b3BfbihuID0gNywgd3QgPSBDb25mUGVyY2FwaXRhKSAlPiUgDQogIHVuZ3JvdXAoKSAlPiUgDQogIG11dGF0ZShTdGF0ZS5VbmlvblRlcnJpdG9yeSA9IGZjdF9yZW9yZGVyKFN0YXRlLlVuaW9uVGVycml0b3J5LCBDb25mUGVyY2FwaXRhLCBtYXgpKSAlPiUgDQoNCmdncGxvdChhZXMoeCA9IENvbmZQZXJjYXBpdGEsIHkgPSBTdGF0ZS5VbmlvblRlcnJpdG9yeSwgDQogICAgICAgICAgIGZpbGwgPSBTdGF0ZS5VbmlvblRlcnJpdG9yeSwgZ3JvdXAgPSBTdGF0ZS5VbmlvblRlcnJpdG9yeSApKSArDQogIGdlb21fY29sKCkgKw0KICB0aGVtZV9jbGVhbigpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCksDQogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKw0KICBmYWNldF93cmFwKH5tb250aCkgKw0KICAjIGNvb3JkX2NhcnRlc2lhbihjbGlwID0gIm9mZiIpICsNCiAgbGFicyh0aXRsZSA9ICJDb25maXJtZWQgY2FzZXMgUGVyY2FwaXRhOiBUb3AgNyBTdGF0ZXMvVVQgZWFjaCBtb250aCAoTWF5IG9ud2FyZHMpIiwNCiAgICAgICBzdWJ0aXRsZSA9ICJEZWxoaSBpcyB3b3JzdCBQZXJmb3JtZWQgYW1vbmcgTWV0cm8gc3RhdGVzICYgb3ZlcmFsbCByZWNlbnRseSAhISIsIA0KICAgICAgIHkgPSAiIiwNCiAgICAgICBjYXB0aW9uID0gIkNyZWF0ZWQgYnkgVmlTYSAhISIpICsNCiAgc2NhbGVfeF9jb250aW51b3VzKGxhYmVscyA9IHNjYWxlczo6cGVyY2VudF9mb3JtYXQoKSkgKw0KICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gIlBhaXJlZCIpDQpgYGANCg0KDQojIyMgVG9wIDcgQ0ZSIFN0YXRlcw0KDQpUb3AgNyBiYXNlZCBvbjogU3RhdGUncyBDRlIgbWF4IHNwaWtlIGluIGVhY2ggbW9udGgNCg0KRXhwb3J0aW5nIGNzdg0KDQpgYGB7ciBldmFsPUZBTFNFfQ0Kd3JpdGUuY3N2KGRmX2luZF9zdGFja2VkX2N1bSwgImRmX2luZF9zdGFja2VkX2N1bS5jc3YiLCByb3cubmFtZXMgPSBGQUxTRSkgDQpgYGANCg0KDQpgYGB7ciBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9MTAsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpkZl9pbmRfc3RhY2tlZF9jdW0gJT4lIA0KICBmaWx0ZXIoIVN0YXRlLlVuaW9uVGVycml0b3J5ICVpbiUgDQogICAgICAgICAgIGMoIk1haGFyYXNodHJhKioqIiwiUHVuamFiKioqIiwiQ2hhbmRpZ2FyaCoqKiIsIlRlbGFuZ2FuYSoqKiIsICJDYXNlcyBiZWluZyByZWFzc2lnbmVkIHRvIHN0YXRlcyIpKSAlPiUNCiAgbXV0YXRlKG1vbnRoID0gbHVicmlkYXRlOjptb250aChEYXRlLCBsYWJlbCA9IFRSVUUsIGFiYnIgPSBUUlVFKSkgJT4lIA0KICBmaWx0ZXIoQ2FzZXNfdHlwZSA9PSAiQ29uZmlybWVkIikgJT4lDQogIGdyb3VwX2J5KFN0YXRlLlVuaW9uVGVycml0b3J5LCBtb250aCkgJT4lIA0KICBzdW1tYXJpc2UoQ0ZSID0gbWF4KENGUiwgbmEucm0gPSBUUlVFKSkgJT4lIA0KICBncm91cF9ieShtb250aCkgJT4lIA0KICB0b3BfbihuID0gNywgd3QgPSBDRlIpICU+JSANCiAgdW5ncm91cCgpICU+JSANCiAgbXV0YXRlKFN0YXRlLlVuaW9uVGVycml0b3J5ID0gZmN0X3Jlb3JkZXIoU3RhdGUuVW5pb25UZXJyaXRvcnksIENGUiwgbWF4KSkgJT4lIA0KDQpnZ3Bsb3QoYWVzKHggPSBDRlIsIHkgPSBTdGF0ZS5VbmlvblRlcnJpdG9yeSwgDQogICAgICAgICAgIGZpbGwgPSBTdGF0ZS5VbmlvblRlcnJpdG9yeSwgZ3JvdXAgPSBTdGF0ZS5VbmlvblRlcnJpdG9yeSApKSArDQogIGdlb21fY29sKCkgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwKSwNCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArDQogIGZhY2V0X3dyYXAofm1vbnRoKSArDQogICMgY29vcmRfY2FydGVzaWFuKGNsaXAgPSAib2ZmIikgKw0KICBzY2FsZV9maWxsX3RhYmxlYXUocGFsZXR0ZSA9ICJUYWJsZWF1IDIwIikgKw0KICBsYWJzKHRpdGxlID0gIkNhc2UgRmF0YWxpdHkgUmF0aW86IFRvcCA3IFN0YXRlcyBlYWNoIG1vbnRoIiwNCiAgICAgICBzdWJ0aXRsZSA9ICJUb3AgNyBiYXNlZCBvbiBTdGF0ZSdzIENGUiBtYXggc3Bpa2UgaW4gZWFjaCBtb250aCIsDQogICAgICAgeCA9ICJDRlIgKCUpIiwNCiAgICAgICBjYXB0aW9uID0gIkNyZWF0ZWQgYnkgVmlTYSAhISIpDQpgYGANCg0KDQojIyMgVG9wIDcgQ0ZSIFN0YXRlcyBNYXkgb253YXJkcw0KDQpgYGB7ciBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9MTAsIG1lc3NhZ2UgPSBGQUxTRX0NCmRmX2luZF9zdGFja2VkX2N1bSAlPiUgDQogIGZpbHRlcighU3RhdGUuVW5pb25UZXJyaXRvcnkgJWluJSANCiAgICAgICAgICAgYygiTWFoYXJhc2h0cmEqKioiLCJQdW5qYWIqKioiLCJDaGFuZGlnYXJoKioqIiwiVGVsYW5nYW5hKioqIiwgIkNhc2VzIGJlaW5nIHJlYXNzaWduZWQgdG8gc3RhdGVzIiksDQogICAgICAgICBEYXRlID4gYXMuRGF0ZSgiMjAyMC0wNC0zMCIpDQogICAgICAgICApICU+JQ0KICBtdXRhdGUobW9udGggPSBsdWJyaWRhdGU6Om1vbnRoKERhdGUsIGxhYmVsID0gVFJVRSwgYWJiciA9IFRSVUUpKSAlPiUgDQogIGZpbHRlcihDYXNlc190eXBlID09ICJDb25maXJtZWQiKSAlPiUNCiAgZ3JvdXBfYnkoU3RhdGUuVW5pb25UZXJyaXRvcnksIG1vbnRoKSAlPiUgDQogIHN1bW1hcmlzZShDRlIgPSBtYXgoQ0ZSLCBuYS5ybSA9IFRSVUUpKSAlPiUgDQogIGdyb3VwX2J5KG1vbnRoKSAlPiUgDQogIHRvcF9uKG4gPSA3LCB3dCA9IENGUikgJT4lIA0KICB1bmdyb3VwKCkgJT4lIA0KICBtdXRhdGUoU3RhdGUuVW5pb25UZXJyaXRvcnkgPSBmY3RfcmVvcmRlcihTdGF0ZS5VbmlvblRlcnJpdG9yeSwgQ0ZSLCBtYXgpKSAlPiUgDQoNCmdncGxvdChhZXMoeCA9IENGUiwgeSA9IFN0YXRlLlVuaW9uVGVycml0b3J5LCANCiAgICAgICAgICAgZmlsbCA9IFN0YXRlLlVuaW9uVGVycml0b3J5LCBncm91cCA9IFN0YXRlLlVuaW9uVGVycml0b3J5ICkpICsNCiAgZ2VvbV9jb2woKSArDQogIHRoZW1lX2NsZWFuKCkgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwKSwNCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArDQogIGZhY2V0X3dyYXAofm1vbnRoKSArDQogICMgY29vcmRfY2FydGVzaWFuKGNsaXAgPSAib2ZmIikgKw0KICBzY2FsZV9maWxsX3RhYmxlYXUocGFsZXR0ZSA9ICJUYWJsZWF1IDIwIikgKw0KICBsYWJzKHRpdGxlID0gIkNGUjogVG9wIDcgU3RhdGVzIGVhY2ggbW9udGggKE1heSBvbndhcmRzKSIsDQogICAgICAgc3VidGl0bGUgPSAiVG9wIDcgYmFzZWQgb24gU3RhdGUncyBtYXggQ0ZSIHNwaWtlIGluIGVhY2ggbW9udGgiLA0KICAgICAgIHggPSAiQ0ZSICglKSIsDQogICAgICAgY2FwdGlvbiA9ICJDcmVhdGVkIGJ5IFZpU2EgISEiKQ0KYGBgDQoNCiMjIyBUb3AgNyBTdGF0ZXMgRGVhdGgNCg0KYmFzZWQgb24gU3RhdGUncyBtYXggc3Bpa2UgaW4gZWFjaCBtb250aA0KDQpgYGB7ciBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9MTAsIG1lc3NhZ2UgPSBGQUxTRX0NCg0KZGZfaW5kX3N0YWNrZWRfY3VtICU+JSANCiAgZmlsdGVyKCFTdGF0ZS5VbmlvblRlcnJpdG9yeSAlaW4lIA0KICAgICAgICAgICBjKCJNYWhhcmFzaHRyYSoqKiIsIlB1bmphYioqKiIsIkNoYW5kaWdhcmgqKioiLCJUZWxhbmdhbmEqKioiLCANCiAgICAgICAgICAgICAiQ2FzZXMgYmVpbmcgcmVhc3NpZ25lZCB0byBzdGF0ZXMiKQ0KICAgICAgICAgKSAlPiUNCiAgbXV0YXRlKG1vbnRoID0gbHVicmlkYXRlOjptb250aChEYXRlLCBsYWJlbCA9IFRSVUUsIGFiYnIgPSBUUlVFKSkgJT4lIA0KICBmaWx0ZXIoQ2FzZXNfdHlwZSA9PSAiRGVhdGhzIikgJT4lDQogIGdyb3VwX2J5KFN0YXRlLlVuaW9uVGVycml0b3J5LCBtb250aCkgJT4lDQogIA0KICBzdW1tYXJpc2UoRGVhdGhzID0gbWF4KENhc2VzX2NvdW50cywgbmEucm0gPSBUUlVFKSkgJT4lIA0KICBncm91cF9ieShtb250aCkgJT4lIA0KICB0b3BfbihuID0gNywgd3QgPSBEZWF0aHMpICU+JSANCiAgdW5ncm91cCgpICU+JSANCiAgbXV0YXRlKFN0YXRlLlVuaW9uVGVycml0b3J5ID0gZmN0X3Jlb3JkZXIoU3RhdGUuVW5pb25UZXJyaXRvcnksIERlYXRocywgbWF4KSkgJT4lIA0KDQpnZ3Bsb3QoYWVzKHggPSBEZWF0aHMsIHkgPSBTdGF0ZS5VbmlvblRlcnJpdG9yeSwgDQogICAgICAgICAgIGZpbGwgPSBTdGF0ZS5VbmlvblRlcnJpdG9yeSwgZ3JvdXAgPSBTdGF0ZS5VbmlvblRlcnJpdG9yeSApKSArDQogIGdlb21fY29sKCkgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwKSwNCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArDQogIGZhY2V0X3dyYXAofm1vbnRoKSArDQogICMgY29vcmRfY2FydGVzaWFuKGNsaXAgPSAib2ZmIikgKw0KICBzY2FsZV9maWxsX3RhYmxlYXUocGFsZXR0ZSA9ICJUYWJsZWF1IDIwIikgKw0KICBsYWJzKHRpdGxlID0gIkRlYXRoczogVG9wIDcgU3RhdGVzIGVhY2ggbW9udGgiLA0KICAgICAgIHN1YnRpdGxlID0gIlRvcCA3IGJhc2VkIG9uIFN0YXRlJ3MgbWF4IERlYXRoIHNwaWtlIGluIGVhY2ggbW9udGgiLA0KICAgICAgIGNhcHRpb24gPSAiQ3JlYXRlZCBieSBWaVNhICEhIikgKw0KICBzY2FsZV94X2NvbnRpbnVvdXMobGFiZWxzID0gc2NhbGVzOjpjb21tYV9mb3JtYXQoKSkgDQpgYGANCg0KDQpgYGB7cn0NCmRmX2luZF9zdGFja2VkX2N1bSAlPiUgcHVsbChDYXNlc190eXBlKSAlPiUgdGFibGUoKQ0KYGBgDQoNCg0KIyMjIFJhdGUgTGluZSBwbG90cw0KDQpgYGB7cn0NCihkZl9pbmRfc3RhY2tlZF9yYXRlcyRTdGF0ZS5VbmlvblRlcnJpdG9yeSAlPiUgdW5pcXVlKCkpWzE6MjJdDQpgYGANCg0KDQpgYGB7ciBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9MTB9DQpkZl9pbmRfc3RhY2tlZF9yYXRlcyAlPiUgDQogIGZpbHRlcighU3RhdGUuVW5pb25UZXJyaXRvcnkgJWluJSANCiAgICAgICAgICAgYygiTWFoYXJhc2h0cmEqKioiLCJQdW5qYWIqKioiLCJDaGFuZGlnYXJoKioqIiwiVGVsYW5nYW5hKioqIiwgDQogICAgICAgICAgICAgIkNhc2VzIGJlaW5nIHJlYXNzaWduZWQgdG8gc3RhdGVzIiwgIlVuYXNzaWduZWQiKQ0KICAgICAgICAgKSAlPiUNCiAgZmlsdGVyKFN0YXRlLlVuaW9uVGVycml0b3J5ICVpbiUgKGRmX2luZF9zdGFja2VkX3JhdGVzJFN0YXRlLlVuaW9uVGVycml0b3J5ICU+JSANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdW5pcXVlKCkpWzE6MjJdKSAlPiUgDQogICMgbXV0YXRlKG1vbnRoID0gbHVicmlkYXRlOjptb250aChEYXRlLCBsYWJlbCA9IFRSVUUsIGFiYnIgPSBUUlVFKSkgJT4lIA0KICAjIGZpbHRlcihDYXNlc190eXBlID09ICJDb25maXJtZWQiKSAlPiUNCiAgDQogIGdncGxvdChhZXMoeCA9IERhdGUsIHkgPSBSYXRlX3ZhbHVlLCBjb2wgPSBSYXRlX3R5cGUpKSArDQogIGdlb21fcGF0aCgpICsNCiAgZmFjZXRfd3JhcCh+U3RhdGUuVW5pb25UZXJyaXRvcnkpICsNCiAgbGFicyh0aXRsZSA9ICJSZWNvdmVyeSBSYXRlICYgQ0ZSIG9mIFN0YXRlczogUGFydCAxIiwNCiAgICAgICB5ID0gImxvZyBvZiBSYXRlKCUpIiwNCiAgICAgICBjYXB0aW9uID0gIkNyZWF0ZWQgYnkgVmlTYSAhISIpICsNCiAgc2NhbGVfeV9sb2cxMCgpDQpgYGANCg0KDQpgYGB7ciBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9MTB9DQpkZl9pbmRfc3RhY2tlZF9yYXRlcyAlPiUgDQogIGZpbHRlcighU3RhdGUuVW5pb25UZXJyaXRvcnkgJWluJSANCiAgICAgICAgICAgYygiTWFoYXJhc2h0cmEqKioiLCJQdW5qYWIqKioiLCJDaGFuZGlnYXJoKioqIiwiVGVsYW5nYW5hKioqIiwgDQogICAgICAgICAgICAgIkNhc2VzIGJlaW5nIHJlYXNzaWduZWQgdG8gc3RhdGVzIiwgIlVuYXNzaWduZWQiKQ0KICAgICAgICAgKSAlPiUNCiAgZmlsdGVyKFN0YXRlLlVuaW9uVGVycml0b3J5ICVpbiUgKGRmX2luZF9zdGFja2VkX3JhdGVzJFN0YXRlLlVuaW9uVGVycml0b3J5ICU+JSANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdW5pcXVlKCkpWzIzOjQzXSkgJT4lIA0KICAjIG11dGF0ZShtb250aCA9IGx1YnJpZGF0ZTo6bW9udGgoRGF0ZSwgbGFiZWwgPSBUUlVFLCBhYmJyID0gVFJVRSkpICU+JSANCiAgIyBmaWx0ZXIoQ2FzZXNfdHlwZSA9PSAiQ29uZmlybWVkIikgJT4lDQogIA0KICBnZ3Bsb3QoYWVzKHggPSBEYXRlLCB5ID0gUmF0ZV92YWx1ZSwgY29sID0gUmF0ZV90eXBlKSkgKw0KICBnZW9tX3BhdGgoKSArDQogIGZhY2V0X3dyYXAoflN0YXRlLlVuaW9uVGVycml0b3J5KSArDQogIGxhYnModGl0bGUgPSAiUmVjb3ZlcnkgUmF0ZSAmIENGUiBvZiBTdGF0ZXM6IFBhcnQgMiIsDQogICAgICAgeSA9ICJsb2cgb2YgUmF0ZSglKSIsDQogICAgICAgY2FwdGlvbiA9ICJDcmVhdGVkIGJ5IFZpU2EgISEiKSArDQogIHNjYWxlX3lfbG9nMTAoKQ0KYGBgDQoNCmBgYHtyfQ0KZGZfaW5kX3N0YWNrZWRfY3VtICU+JSANCiAgZmlsdGVyKCFTdGF0ZS5VbmlvblRlcnJpdG9yeSAlaW4lIA0KICAgICAgICAgICBjKCJNYWhhcmFzaHRyYSoqKiIsIlB1bmphYioqKiIsIkNoYW5kaWdhcmgqKioiLCJUZWxhbmdhbmEqKioiLCANCiAgICAgICAgICAgICAiQ2FzZXMgYmVpbmcgcmVhc3NpZ25lZCB0byBzdGF0ZXMiKQ0KICAgICAgICAgKSAlPiUNCiAgbXV0YXRlKG1vbnRoID0gbHVicmlkYXRlOjptb250aChEYXRlLCBsYWJlbCA9IFRSVUUsIGFiYnIgPSBUUlVFKSkgJT4lIA0KICBncm91cF9ieShTdGF0ZS5VbmlvblRlcnJpdG9yeSwgbW9udGgsIENhc2VzX3R5cGUpICU+JQ0KICANCiAgc3VtbWFyaXNlKENhc2VzX2NvdW50cyA9IG1heChDYXNlc19jb3VudHMsIG5hLnJtID0gVFJVRSkpICU+JSANCiAgZ3JvdXBfYnkobW9udGgsIENhc2VzX3R5cGUpICU+JQ0KICB0b3BfbihuID0gNywgd3QgPSBDYXNlc19jb3VudHMpICU+JSAjcHVsbChTdGF0ZS5VbmlvblRlcnJpdG9yeSkgJT4lIHVuaXF1ZSgpIA0KICANCiAgbXV0YXRlKENhc2VzX3NpemUgPSBDYXNlc19jb3VudHMgLyBzdW0oQ2FzZXNfY291bnRzKSkgJT4lIA0KICANCiAgd3JpdGUuY3N2KCJkZl9pbmRfc3RhY2tlZF9jdW0uY3N2Iiwgcm93Lm5hbWVzID0gRkFMU0UpDQpgYGANCg0KDQpgYGB7ciBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9MTAsIG1lc3NhZ2UgPSBGQUxTRX0NCg0KZGZfaW5kX3N0YWNrZWRfY3VtICU+JSANCiAgZmlsdGVyKCFTdGF0ZS5VbmlvblRlcnJpdG9yeSAlaW4lIA0KICAgICAgICAgICBjKCJNYWhhcmFzaHRyYSoqKiIsIlB1bmphYioqKiIsIkNoYW5kaWdhcmgqKioiLCJUZWxhbmdhbmEqKioiLCANCiAgICAgICAgICAgICAiQ2FzZXMgYmVpbmcgcmVhc3NpZ25lZCB0byBzdGF0ZXMiKQ0KICAgICAgICAgKSAlPiUNCiAgbXV0YXRlKG1vbnRoID0gbHVicmlkYXRlOjptb250aChEYXRlLCBsYWJlbCA9IFRSVUUsIGFiYnIgPSBUUlVFKSkgJT4lIA0KICBncm91cF9ieShTdGF0ZS5VbmlvblRlcnJpdG9yeSwgbW9udGgsIENhc2VzX3R5cGUpICU+JQ0KICANCiAgc3VtbWFyaXNlKENhc2VzX2NvdW50cyA9IG1heChDYXNlc19jb3VudHMsIG5hLnJtID0gVFJVRSkpICU+JSANCiAgZ3JvdXBfYnkobW9udGgsIENhc2VzX3R5cGUpICU+JQ0KICB0b3BfbihuID0gNywgd3QgPSBDYXNlc19jb3VudHMpICU+JSAjcHVsbChTdGF0ZS5VbmlvblRlcnJpdG9yeSkgJT4lIHVuaXF1ZSgpIA0KICANCiAgbXV0YXRlKENhc2VzX3NpemUgPSBDYXNlc19jb3VudHMgLyBzdW0oQ2FzZXNfY291bnRzKSkgJT4lIA0KICBtdXRhdGUoU3RhdGUuVW5pb25UZXJyaXRvcnkgPSBmY3RfcmVvcmRlcihTdGF0ZS5VbmlvblRlcnJpdG9yeSwgQ2FzZXNfY291bnRzLCBtYXgpKSAlPiUgDQoNCiAgZ2dwbG90KGFlcyh4ID0gQ2FzZXNfdHlwZSwgeSA9IFN0YXRlLlVuaW9uVGVycml0b3J5KSkgKw0KICBnZW9tX3BvaW50KHNoYXBlID0gMjEsICMgYWxwaGEgPSAwLjYsIA0KICAgICAgICAgICAgIGFlcyhzaXplID0gQ2FzZXNfc2l6ZSwgY29sb3IgPSBDYXNlc190eXBlKSwgZmlsbCA9ICIjRkZFQkNEIiwgc3Ryb2tlID0gMykgKw0KICB0aGVtZV93c2ooKSArDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTApLA0KICAgICAgICB0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLA0KICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIg0KICAgICAgICApICsNCiAgZmFjZXRfd3JhcCh+bW9udGgpICsNCiAgY29vcmRfY2FydGVzaWFuKGNsaXAgPSAib2ZmIikgKw0KICBzY2FsZV9jb2xvcl90YWJsZWF1KHBhbGV0dGUgPSAiVGFibGVhdSAxMCIpICsNCiAgbGFicyh0aXRsZSA9ICJUb3AgNyBTdGF0ZXMgZWFjaCBtb250aDogQ29uZmlybWVkIHwgUmVjb3ZlcmVkIHwgRGVhdGhzIiwNCiAgICAgICBzdWJ0aXRsZSA9ICJQb2ludCBTaXplIHZhcmllcyBiYXNlZCBvbiByZXNwZWN0aXZlIG1vbnRoIGNhc2VzIHR5cGUgd2l0aGluIGVhY2ggY2F0ZWdvcnkNCiAgICAgICBUb3AgNyBiYXNlZCBvbiBTdGF0ZSdzIG1heCBzcGlrZSBpbiBlYWNoIGNhdGVnb3J5IGVhY2ggbW9udGgiLA0KICAgICAgIGNhcHRpb24gPSAiQ3JlYXRlZCBieSBWaVNhICEhIikgDQogICMgc2NhbGVfeF9jb250aW51b3VzKGxhYmVscyA9IHNjYWxlczo6Y29tbWFfZm9ybWF0KCkpIA0KYGBgDQoNCg0KYGBge3IgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTEwLCBtZXNzYWdlID0gRkFMU0V9DQoNCmRmX2luZF9zdGFja2VkX2N1bSAlPiUgDQogIGZpbHRlcighU3RhdGUuVW5pb25UZXJyaXRvcnkgJWluJSANCiAgICAgICAgICAgYygiTWFoYXJhc2h0cmEqKioiLCJQdW5qYWIqKioiLCJDaGFuZGlnYXJoKioqIiwiVGVsYW5nYW5hKioqIiwgDQogICAgICAgICAgICAgIkNhc2VzIGJlaW5nIHJlYXNzaWduZWQgdG8gc3RhdGVzIikNCiAgICAgICAgICkgJT4lDQogIG11dGF0ZShtb250aCA9IGx1YnJpZGF0ZTo6bW9udGgoRGF0ZSwgbGFiZWwgPSBUUlVFLCBhYmJyID0gVFJVRSkpICU+JSANCiAgZ3JvdXBfYnkoU3RhdGUuVW5pb25UZXJyaXRvcnksIG1vbnRoLCBDYXNlc190eXBlKSAlPiUNCiAgDQogIHN1bW1hcmlzZShDYXNlc19jb3VudHMgPSBtYXgoQ2FzZXNfY291bnRzLCBuYS5ybSA9IFRSVUUpKSAlPiUgDQogIGdyb3VwX2J5KG1vbnRoLCBDYXNlc190eXBlKSAlPiUNCiAgdG9wX24obiA9IDUsIHd0ID0gQ2FzZXNfY291bnRzKSAlPiUgI3B1bGwoU3RhdGUuVW5pb25UZXJyaXRvcnkpICU+JSB1bmlxdWUoKSANCiAgDQogIG11dGF0ZShDYXNlc19zaXplID0gQ2FzZXNfY291bnRzIC8gc3VtKENhc2VzX2NvdW50cykpICU+JSANCiAgbXV0YXRlKFN0YXRlLlVuaW9uVGVycml0b3J5ID0gZmN0X3Jlb3JkZXIoU3RhdGUuVW5pb25UZXJyaXRvcnksIENhc2VzX2NvdW50cywgbWF4KSkgJT4lIA0KDQogIGdncGxvdChhZXMoeCA9IENhc2VzX3R5cGUsIHkgPSBTdGF0ZS5VbmlvblRlcnJpdG9yeSkpICsNCiAgZ2VvbV9wb2ludChzaGFwZSA9IDIxLCAjIGFscGhhID0gMC42LCANCiAgICAgICAgICAgICBhZXMoc2l6ZSA9IENhc2VzX3NpemUsIGNvbG9yID0gQ2FzZXNfdHlwZSksIGZpbGwgPSAiI0ZGRUJDRCIsIHN0cm9rZSA9IDMpICsNCiAgdGhlbWVfd3NqKCkgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwKSwNCiAgICAgICAgdGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwNCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSINCiAgICAgICAgKSArDQogIGZhY2V0X3dyYXAofm1vbnRoKSArDQogIGNvb3JkX2NhcnRlc2lhbihjbGlwID0gIm9mZiIpICsNCiAgc2NhbGVfY29sb3JfdGFibGVhdShwYWxldHRlID0gIlRhYmxlYXUgMTAiKSArDQogIGxhYnModGl0bGUgPSAiVG9wIDUgU3RhdGVzIGVhY2ggbW9udGg6IENvbmZpcm1lZCB8IFJlY292ZXJlZCB8IERlYXRocyIsDQogICAgICAgc3VidGl0bGUgPSAiUG9pbnQgU2l6ZSB2YXJpZXMgYmFzZWQgb24gcmVzcGVjdGl2ZSBtb250aCBjYXNlcyB0eXBlIHdpdGhpbiBlYWNoIGNhdGVnb3J5DQogICAgICAgVG9wIDUgYmFzZWQgb24gU3RhdGUncyBtYXggc3Bpa2UgaW4gZWFjaCBjYXRlZ29yeSBlYWNoIG1vbnRoIiwNCiAgICAgICBjYXB0aW9uID0gIkNyZWF0ZWQgYnkgVmlTYSAhISIpIA0KICAjIHNjYWxlX3hfY29udGludW91cyhsYWJlbHMgPSBzY2FsZXM6OmNvbW1hX2Zvcm1hdCgpKSANCmBgYA0KDQoNCiMjIyBUb3AgMyBTdGF0ZXM6IENGUiwgUmVjb3ZlcnkgcmF0ZSBlYWNoIG1vbnRoDQoNCmBgYHtyIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD0xNCwgbWVzc2FnZSA9IEZBTFNFfQ0KZGZfaW5kX3N0YWNrZWRfcmF0ZXMgJT4lIA0KICBmaWx0ZXIoIVN0YXRlLlVuaW9uVGVycml0b3J5ICVpbiUgDQogICAgICAgICAgIGMoIk1haGFyYXNodHJhKioqIiwiUHVuamFiKioqIiwiQ2hhbmRpZ2FyaCoqKiIsIlRlbGFuZ2FuYSoqKiIsIA0KICAgICAgICAgICAgICJDYXNlcyBiZWluZyByZWFzc2lnbmVkIHRvIHN0YXRlcyIpDQogICAgICAgICApICU+JQ0KICBtdXRhdGUobW9udGggPSBsdWJyaWRhdGU6Om1vbnRoKERhdGUsIGxhYmVsID0gVFJVRSwgYWJiciA9IFRSVUUpKSAlPiUgDQogIA0KICBncm91cF9ieShTdGF0ZS5VbmlvblRlcnJpdG9yeSwgbW9udGgpICU+JSANCiAgc3VtbWFyaXNlKE5ld19DRlIgPSByb3VuZChzdW0oRGVhdGhzKSAvIHN1bShDb25maXJtZWQpLCBkaWdpdHMgPSA0KSwNCiAgICAgICAgICAgIE5ld19SZWNvdmVyeV9SYXRlID0gcm91bmQoc3VtKEN1cmVkKSAvIHN1bShDb25maXJtZWQpLCBkaWdpdHMgPSA0KSkgJT4lIA0KICAjIG11dGF0ZShOZXdfQ0ZSID0gcmVwbGFjZShOZXdfQ0ZSLCBpcy5uYShOZXdfQ0ZSKSwgMCksDQogICMgICAgICAgIE5ld19SZWNvdmVyeV9SYXRlID0gcmVwbGFjZShOZXdfUmVjb3ZlcnlfUmF0ZSwgaXMubmEoTmV3X1JlY292ZXJ5X1JhdGUpLCAwKSkgJT4lIA0KICANCiAgZ2F0aGVyKGtleSA9IFJhdGVfdHlwZSwgdmFsdWUgPSBSYXRlX3ZhbHVlLCAtYygiU3RhdGUuVW5pb25UZXJyaXRvcnkiLCAibW9udGgiKSkgJT4lIA0KICBtdXRhdGUoUmF0ZV90eXBlID0gcmVwbGFjZShSYXRlX3R5cGUsIFJhdGVfdHlwZSA9PSAiTmV3X0NGUiIsIkNGUiIpLA0KICAgICAgICAgUmF0ZV90eXBlID0gcmVwbGFjZShSYXRlX3R5cGUsIFJhdGVfdHlwZSA9PSAiTmV3X1JlY292ZXJ5X1JhdGUiLCJSZWNvdmVyeV9SYXRlIiksDQogICAgICAgICBSYXRlX3R5cGUgPSBhcy5mYWN0b3IoUmF0ZV90eXBlKSkgJT4lIA0KDQogIGdyb3VwX2J5KG1vbnRoLCBSYXRlX3R5cGUpICU+JQ0KICB0b3BfbihuID0gMywgd3QgPSBSYXRlX3ZhbHVlKSAlPiUgDQogIA0KICBtdXRhdGUoQ2FzZXNfc2l6ZSA9IFJhdGVfdmFsdWUgLyBzdW0oUmF0ZV92YWx1ZSksDQogICAgICAgICBDYXNlc19zaXplID0gcmVwbGFjZShDYXNlc19zaXplLCBpcy5uYShDYXNlc19zaXplKSwgMCkpICAlPiUgDQogIA0KICBtdXRhdGUoU3RhdGUuVW5pb25UZXJyaXRvcnkgPSBmY3RfcmVvcmRlcihTdGF0ZS5VbmlvblRlcnJpdG9yeSwgUmF0ZV92YWx1ZSwgbWF4KSkgJT4lIA0KDQogIG11dGF0ZShTdGF0ZS5VbmlvblRlcnJpdG9yeSA9IGFzLmNoYXJhY3RlcihTdGF0ZS5VbmlvblRlcnJpdG9yeSksIA0KICAgICAgICAgU3RhdGUuVW5pb25UZXJyaXRvcnkgPSByZXBsYWNlKFN0YXRlLlVuaW9uVGVycml0b3J5LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFN0YXRlLlVuaW9uVGVycml0b3J5ID09ICJEYWRyYSBhbmQgTmFnYXIgSGF2ZWxpIGFuZCBEYW1hbiBhbmQgRGl1IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRGFkcmEgRGFtYW4mIERpdSIpLA0KICAgICAgICAgU3RhdGUuVW5pb25UZXJyaXRvcnkgPSBhcy5mYWN0b3IoU3RhdGUuVW5pb25UZXJyaXRvcnkpKSAlPiUgDQogIHVuZ3JvdXAoKSAlPiUgI3B1bGwoU3RhdGUuVW5pb25UZXJyaXRvcnkpICU+JSB1bmlxdWUoKQ0KDQogIGdncGxvdChhZXMoeCA9IFJhdGVfdHlwZSwgeSA9IFN0YXRlLlVuaW9uVGVycml0b3J5KSkgKw0KICBnZW9tX3BvaW50KHNoYXBlID0gMjEsICMgYWxwaGEgPSAwLjYsIA0KICAgICAgICAgICAgIGFlcyhzaXplID0gQ2FzZXNfc2l6ZSwgY29sb3IgPSBSYXRlX3R5cGUpLCBmaWxsID0gIiNGRkVCQ0QiLCBzdHJva2UgPSAzKSArDQogIHRoZW1lX3dzaigpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCksDQogICAgICAgIHRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksDQogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLA0KICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpDQogICAgICAgICkgKw0KICBmYWNldF93cmFwKH5tb250aCkgKw0KICBjb29yZF9jYXJ0ZXNpYW4oY2xpcCA9ICJvZmYiKSArDQogIHNjYWxlX2NvbG9yX3RhYmxlYXUocGFsZXR0ZSA9ICJUYWJsZWF1IDEwIikgKw0KICBsYWJzKHRpdGxlID0gIlRvcCAzIFN0YXRlcyBlYWNoIG1vbnRoOiBDYXNlIEZhdGFsaXR5IFJhdGUgfCBSZWNvdmVyeSBSYXRlIiwNCiAgICAgICBzdWJ0aXRsZSA9ICJQb2ludCBTaXplIHZhcmllcyBiYXNlZCBvbiByZXNwZWN0aXZlIG1vbnRoIGNhc2VzIHR5cGUgd2l0aGluIGVhY2ggY2F0ZWdvcnkiLA0KICAgICAgIGNhcHRpb24gPSAiQ3JlYXRlZCBieSBWaVNhICEhIikNCiAgIyBzY2FsZV94X2NvbnRpbnVvdXMobGFiZWxzID0gc2NhbGVzOjpjb21tYV9mb3JtYXQoKSkgIA0KICANCmBgYA0KDQoNCmBgYHtyfQ0KZGZfaW5kX3N0YWNrZWRfcmF0ZXMgJT4lIA0KICBmaWx0ZXIoIVN0YXRlLlVuaW9uVGVycml0b3J5ICVpbiUgDQogICAgICAgICAgIGMoIk1haGFyYXNodHJhKioqIiwiUHVuamFiKioqIiwiQ2hhbmRpZ2FyaCoqKiIsIlRlbGFuZ2FuYSoqKiIsIA0KICAgICAgICAgICAgICJDYXNlcyBiZWluZyByZWFzc2lnbmVkIHRvIHN0YXRlcyIpDQogICAgICAgICApICU+JQ0KICBtdXRhdGUobW9udGggPSBsdWJyaWRhdGU6Om1vbnRoKERhdGUsIGxhYmVsID0gVFJVRSwgYWJiciA9IFRSVUUpKSAlPiUgDQogIA0KICBncm91cF9ieShTdGF0ZS5VbmlvblRlcnJpdG9yeSwgbW9udGgpICU+JSANCiAgc3VtbWFyaXNlKE5ld19DRlIgPSByb3VuZChzdW0oRGVhdGhzKSAvIHN1bShDb25maXJtZWQpLCBkaWdpdHMgPSA0KSwNCiAgICAgICAgICAgIE5ld19SZWNvdmVyeV9SYXRlID0gcm91bmQoc3VtKEN1cmVkKSAvIHN1bShDb25maXJtZWQpLCBkaWdpdHMgPSA0KSkgJT4lIA0KICAjIG11dGF0ZShOZXdfQ0ZSID0gcmVwbGFjZShOZXdfQ0ZSLCBpcy5uYShOZXdfQ0ZSKSwgMCksDQogICMgICAgICAgIE5ld19SZWNvdmVyeV9SYXRlID0gcmVwbGFjZShOZXdfUmVjb3ZlcnlfUmF0ZSwgaXMubmEoTmV3X1JlY292ZXJ5X1JhdGUpLCAwKSkgJT4lIA0KICANCiAgZ2F0aGVyKGtleSA9IFJhdGVfdHlwZSwgdmFsdWUgPSBSYXRlX3ZhbHVlLCAtYygiU3RhdGUuVW5pb25UZXJyaXRvcnkiLCAibW9udGgiKSkgJT4lIA0KICBtdXRhdGUoUmF0ZV90eXBlID0gcmVwbGFjZShSYXRlX3R5cGUsIFJhdGVfdHlwZSA9PSAiTmV3X0NGUiIsIkNGUiIpLA0KICAgICAgICAgUmF0ZV90eXBlID0gcmVwbGFjZShSYXRlX3R5cGUsIFJhdGVfdHlwZSA9PSAiTmV3X1JlY292ZXJ5X1JhdGUiLCJSZWNvdmVyeV9SYXRlIiksDQogICAgICAgICBSYXRlX3R5cGUgPSBhcy5mYWN0b3IoUmF0ZV90eXBlKSkgJT4lIA0KDQogIGdyb3VwX2J5KG1vbnRoLCBSYXRlX3R5cGUpICU+JQ0KICAjIHRvcF9uKG4gPSAzLCB3dCA9IFJhdGVfdmFsdWUpICU+JSANCiAgDQogIG11dGF0ZShDYXNlc19zaXplID0gUmF0ZV92YWx1ZSAvIHN1bShSYXRlX3ZhbHVlKSwNCiAgICAgICAgIENhc2VzX3NpemUgPSByZXBsYWNlKENhc2VzX3NpemUsIGlzLm5hKENhc2VzX3NpemUpLCAwKSkgICU+JSANCiAgDQogIG11dGF0ZShTdGF0ZS5VbmlvblRlcnJpdG9yeSA9IGZjdF9yZW9yZGVyKFN0YXRlLlVuaW9uVGVycml0b3J5LCBSYXRlX3ZhbHVlLCBtYXgpKSAlPiUgDQoNCiAgbXV0YXRlKFN0YXRlLlVuaW9uVGVycml0b3J5ID0gYXMuY2hhcmFjdGVyKFN0YXRlLlVuaW9uVGVycml0b3J5KSwgDQogICAgICAgICBTdGF0ZS5VbmlvblRlcnJpdG9yeSA9IHJlcGxhY2UoU3RhdGUuVW5pb25UZXJyaXRvcnksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgU3RhdGUuVW5pb25UZXJyaXRvcnkgPT0gIkRhZHJhIGFuZCBOYWdhciBIYXZlbGkgYW5kIERhbWFuIGFuZCBEaXUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJEYWRyYSBEYW1hbiYgRGl1IiksDQogICAgICAgICBTdGF0ZS5VbmlvblRlcnJpdG9yeSA9IGFzLmZhY3RvcihTdGF0ZS5VbmlvblRlcnJpdG9yeSkpICU+JSANCiAgdW5ncm91cCgpICU+JSANCiAgZmlsdGVyKG1vbnRoID09ICJNYXkiLCBSYXRlX3R5cGUgPT0gIkNGUiIpICU+JSANCiAgYXJyYW5nZShkZXNjKFJhdGVfdmFsdWUpKQ0KYGBgDQoNCg0KYGBge3IgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTEwLCBtZXNzYWdlID0gRkFMU0V9DQpkZl9pbmRfc3RhY2tlZF9jdW0gJT4lIA0KICBmaWx0ZXIoIVN0YXRlLlVuaW9uVGVycml0b3J5ICVpbiUgDQogICAgICAgICAgIGMoIk1haGFyYXNodHJhKioqIiwiUHVuamFiKioqIiwiQ2hhbmRpZ2FyaCoqKiIsIlRlbGFuZ2FuYSoqKiIsICJDYXNlcyBiZWluZyByZWFzc2lnbmVkIHRvIHN0YXRlcyIpLA0KICAgICAgICAgRGF0ZSA+IGFzLkRhdGUoIjIwMjAtMDQtMzAiKQ0KICAgICAgICAgKSAlPiUNCiAgbXV0YXRlKG1vbnRoID0gbHVicmlkYXRlOjptb250aChEYXRlLCBsYWJlbCA9IFRSVUUsIGFiYnIgPSBUUlVFKSkgJT4lIA0KICBmaWx0ZXIoQ2FzZXNfdHlwZSA9PSAiQ29uZmlybWVkIikgJT4lDQogIGdyb3VwX2J5KFN0YXRlLlVuaW9uVGVycml0b3J5LCBtb250aCkgJT4lIA0KICBzdW1tYXJpc2UoQ2FzZXNfY291bnRzID0gbWF4KENhc2VzX2NvdW50cywgbmEucm0gPSBUUlVFKSwNCiAgICAgICAgICAgIENGUiA9IGxhc3QoQ0ZSKSkgJT4lIA0KICBncm91cF9ieShtb250aCkgJT4lIA0KICB0b3BfbihuID0gNywgd3QgPSBDRlIpICU+JSANCiAgdW5ncm91cCgpICU+JSANCiAgbXV0YXRlKFN0YXRlLlVuaW9uVGVycml0b3J5ID0gZmN0X3Jlb3JkZXIoU3RhdGUuVW5pb25UZXJyaXRvcnksIENGUiwgbWF4KSkgJT4lIA0KICANCiAgIyBmaWx0ZXIobW9udGggPT0gIk1heSIpDQoNCmdncGxvdChhZXMoeCA9IENGUiwgeSA9IFN0YXRlLlVuaW9uVGVycml0b3J5LCANCiAgICAgICAgICAgZmlsbCA9IFN0YXRlLlVuaW9uVGVycml0b3J5LCBncm91cCA9IFN0YXRlLlVuaW9uVGVycml0b3J5ICkpICsNCiAgZ2VvbV9jb2woKSArDQogIHRoZW1lX2NsZWFuKCkgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwKSwNCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArDQogIGZhY2V0X3dyYXAofm1vbnRoKSArDQogICMgY29vcmRfY2FydGVzaWFuKGNsaXAgPSAib2ZmIikgKw0KICBzY2FsZV9maWxsX3RhYmxlYXUocGFsZXR0ZSA9ICJUYWJsZWF1IDIwIikgKw0KICBsYWJzKHRpdGxlID0gIkNGUjogVG9wIDcgU3RhdGVzIGVhY2ggbW9udGggKE1heSBvbndhcmRzKSIsDQogICAgICAgeCA9ICJDRlIgKCUpIiwNCiAgICAgICBjYXB0aW9uID0gIkNyZWF0ZWQgYnkgVmlTYSAhISIpDQpgYGANCg0KDQpgYGB7cn0NCmRmX2luZF9zdGFja2VkX2N1bSAlPiUgDQogIGZpbHRlcighU3RhdGUuVW5pb25UZXJyaXRvcnkgJWluJSANCiAgICAgICAgICAgYygiTWFoYXJhc2h0cmEqKioiLCJQdW5qYWIqKioiLCJDaGFuZGlnYXJoKioqIiwiVGVsYW5nYW5hKioqIiwgIkNhc2VzIGJlaW5nIHJlYXNzaWduZWQgdG8gc3RhdGVzIiksDQogICAgICAgICBEYXRlID4gYXMuRGF0ZSgiMjAyMC0wNC0zMCIpDQogICAgICAgICApICU+JQ0KICBtdXRhdGUobW9udGggPSBsdWJyaWRhdGU6Om1vbnRoKERhdGUsIGxhYmVsID0gVFJVRSwgYWJiciA9IFRSVUUpKSAlPiUgDQogIGZpbHRlcihDYXNlc190eXBlID09ICJDb25maXJtZWQiKSAlPiUNCiAgZ3JvdXBfYnkoU3RhdGUuVW5pb25UZXJyaXRvcnksIG1vbnRoKSAlPiUgDQogIA0KICBmaWx0ZXIobW9udGggPT0gIk1heSIsIFN0YXRlLlVuaW9uVGVycml0b3J5ID09ICJQdWR1Y2hlcnJ5IikNCmBgYA0KDQpGaW5kaW5nIExhc3QgZGF0ZXMgb2YgbW9udGhzDQoNCmZyb206IGh0dHBzOi8vd3d3LnJvZWxwZXRlcnMuYmUvZ2V0LXRoZS1maXJzdC1kYXktb3ItdGhlLWxhc3QtZGF5LW9mLXRoZS1tb250aC1pbi1yLXVzaW5nLWx1YnJpZGF0ZS8NCg0KYGBge3J9DQpkZl9pbmRfc3RhY2tlZF9jdW0gJT4lIA0KICBwdWxsKERhdGUpICU+JSANCiAgdW5pcXVlKCkgJT4lIA0KICB5bWQoKSAlPiUNCiAgY2VpbGluZ19kYXRlKCJtb250aCIpLTENCmBgYA0KDQoNCmBgYHtyIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD0xMCwgbWVzc2FnZSA9IEZBTFNFfQ0KZGZfaW5kX3N0YWNrZWRfY3VtICU+JSANCiAgZmlsdGVyKCFTdGF0ZS5VbmlvblRlcnJpdG9yeSAlaW4lIA0KICAgICAgICAgICBjKCJNYWhhcmFzaHRyYSoqKiIsIlB1bmphYioqKiIsIkNoYW5kaWdhcmgqKioiLCJUZWxhbmdhbmEqKioiLCAiQ2FzZXMgYmVpbmcgcmVhc3NpZ25lZCB0byBzdGF0ZXMiKSwNCiAgICAgICAgIERhdGUgPiBhcy5EYXRlKCIyMDIwLTA0LTMwIikNCiAgICAgICAgICkgJT4lDQogIG11dGF0ZShtb250aCA9IGx1YnJpZGF0ZTo6bW9udGgoRGF0ZSwgbGFiZWwgPSBUUlVFLCBhYmJyID0gVFJVRSkpICU+JSANCiAgZmlsdGVyKENhc2VzX3R5cGUgPT0gIkNvbmZpcm1lZCIsDQogICAgICAgICBEYXRlID09IGNlaWxpbmdfZGF0ZShEYXRlLCAibW9udGgiKSAtMSkgJT4lDQogIGdyb3VwX2J5KFN0YXRlLlVuaW9uVGVycml0b3J5LCBtb250aCkgJT4lIA0KICBzdW1tYXJpc2UoQ0ZSID0gbWF4KENGUiwgbmEucm0gPSBUUlVFKSkgJT4lIA0KICBncm91cF9ieShtb250aCkgJT4lIA0KICB0b3BfbihuID0gNywgd3QgPSBDRlIpICU+JSANCiAgdW5ncm91cCgpICU+JSANCiAgbXV0YXRlKFN0YXRlLlVuaW9uVGVycml0b3J5ID0gZmN0X3Jlb3JkZXIoU3RhdGUuVW5pb25UZXJyaXRvcnksIENGUiwgbWF4KSkgJT4lIA0KICANCiAgIyBmaWx0ZXIobW9udGggPT0gIk1heSIpDQoNCmdncGxvdChhZXMoeCA9IENGUiwgeSA9IFN0YXRlLlVuaW9uVGVycml0b3J5LCANCiAgICAgICAgICAgZmlsbCA9IFN0YXRlLlVuaW9uVGVycml0b3J5LCBncm91cCA9IFN0YXRlLlVuaW9uVGVycml0b3J5ICkpICsNCiAgZ2VvbV9jb2woKSArDQogIHRoZW1lX2NsZWFuKCkgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwKSwNCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArDQogIGZhY2V0X3dyYXAofm1vbnRoKSArDQogICMgY29vcmRfY2FydGVzaWFuKGNsaXAgPSAib2ZmIikgKw0KICBzY2FsZV9maWxsX3RhYmxlYXUocGFsZXR0ZSA9ICJUYWJsZWF1IDIwIikgKw0KICBsYWJzKHRpdGxlID0gIkNGUjogVG9wIDcgU3RhdGVzIGVhY2ggbW9udGggKE1heSBvbndhcmRzKSIsDQogICAgICAgeCA9ICJDRlIgKCUpIiwNCiAgICAgICBjYXB0aW9uID0gIkNyZWF0ZWQgYnkgVmlTYSAhISIpDQpgYGANCg0KYGBge3IgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTEwLCBtZXNzYWdlID0gRkFMU0V9DQpkZl9pbmRfc3RhY2tlZF9jdW0gJT4lIA0KICBmaWx0ZXIoIVN0YXRlLlVuaW9uVGVycml0b3J5ICVpbiUgDQogICAgICAgICAgIGMoIk1haGFyYXNodHJhKioqIiwiUHVuamFiKioqIiwiQ2hhbmRpZ2FyaCoqKiIsIlRlbGFuZ2FuYSoqKiIsICJDYXNlcyBiZWluZyByZWFzc2lnbmVkIHRvIHN0YXRlcyIpLA0KICAgICAgICAgRGF0ZSA+IGFzLkRhdGUoIjIwMjAtMDQtMzAiKQ0KICAgICAgICAgKSAlPiUNCiAgbXV0YXRlKG1vbnRoID0gbHVicmlkYXRlOjptb250aChEYXRlLCBsYWJlbCA9IFRSVUUsIGFiYnIgPSBUUlVFKSkgJT4lIA0KICBmaWx0ZXIoQ2FzZXNfdHlwZSA9PSAiQ29uZmlybWVkIiwNCiAgICAgICAgIERhdGUgPT0gY2VpbGluZ19kYXRlKERhdGUsICJtb250aCIpIC0xKSAlPiUNCiAgZ3JvdXBfYnkoU3RhdGUuVW5pb25UZXJyaXRvcnksIG1vbnRoKSAlPiUgDQogIHN1bW1hcmlzZShDRlIgPSBtYXgoQ0ZSLCBuYS5ybSA9IFRSVUUpKSAlPiUgDQogIGdyb3VwX2J5KG1vbnRoKSAlPiUgI2ZpbHRlcihtb250aCA9PSAiTWF5IikgJT4lIGFycmFuZ2UoZGVzYyhDRlIpKQ0KICB0b3BfbihuID0gNywgd3QgPSBDRlIpICU+JSANCiAgdW5ncm91cCgpICU+JSANCiAgbXV0YXRlKFN0YXRlLlVuaW9uVGVycml0b3J5ID0gZmN0X3Jlb3JkZXIoU3RhdGUuVW5pb25UZXJyaXRvcnksIENGUiwgbWF4KSkgJT4lIA0KICANCiAgIyBmaWx0ZXIobW9udGggPT0gIk1heSIpDQoNCmdncGxvdChhZXMoeCA9IENGUiwgeSA9IFN0YXRlLlVuaW9uVGVycml0b3J5LCANCiAgICAgICAgICAgZmlsbCA9IFN0YXRlLlVuaW9uVGVycml0b3J5LCBncm91cCA9IFN0YXRlLlVuaW9uVGVycml0b3J5ICkpICsNCiAgZ2VvbV9jb2woKSArDQogIHRoZW1lX2NsZWFuKCkgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwKSwNCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArDQogIGZhY2V0X3dyYXAofm1vbnRoKSArDQogICMgY29vcmRfY2FydGVzaWFuKGNsaXAgPSAib2ZmIikgKw0KICBzY2FsZV9maWxsX3RhYmxlYXUocGFsZXR0ZSA9ICJUYWJsZWF1IDIwIikgKw0KICBsYWJzKHRpdGxlID0gIkNGUjogVG9wIDcgU3RhdGVzIGVhY2ggbW9udGggKE1heSBvbndhcmRzKSIsDQogICAgICAgeCA9ICJDRlIgKCUpIiwNCiAgICAgICBjYXB0aW9uID0gIkNyZWF0ZWQgYnkgVmlTYSAhISIpDQpgYGANCg0KDQpgYGB7cn0NCmRmX2luZCAlPiUgZmlsdGVyKERhaWx5X2NvbmZpcm1lZCA8IDApDQpgYGANCg0KKipDdW11bGF0aXZlIE51bWJlcnMgZmFsbHMgZm9yIGZldyBzdGF0ZXMgYW5kIGNyZWF0ZXMgbmVnYXRpdmUgZGFpbHlfY29uZmlybWVkIGNhc2VzLioqDQoNCg0KYGBge3J9DQpkZl9pbmQgJT4lIA0KICBmaWx0ZXIoRGF0ZSAlaW4lIGMoYXMuRGF0ZSgiMjAyMC0wMy0wOSIpIDogYXMuRGF0ZSgiMjAyMC0wMy0xNCIpKSwNCiAgICAgICAgIFN0YXRlLlVuaW9uVGVycml0b3J5ID09ICJNYWhhcmFzaHRyYSIpDQpgYGANCg0KIyMjIENvbmNsdXNpb24NCg0KKipDYWxjdWxhdGlvbiBpbiBDRlIgZ2V0cyBtaXNtYXRjaGVkIG51bWJlcnMgd2hlbiBjYWxjdWxhdGVkIGJ5OioqDQoxLiBDRlIgY2FsY3VsYXRlZCBvbiBkYWlseSBiYXNpcyBhbmQgc2VsZWN0ZWQgQ0ZSIG9uIG1heChDb25maXJtZWQpIG9mIG1vbnRoIHdoaWNoIHNob3VsZCBiZSBjb3JyZWN0IGlmIGN1bXVsYXRpdmUgb2YgZmlndXJlcyBpcyBjb3JyZWN0Lg0KDQoyLiBDRlIgY2FsY3VsYXRlZCBieSBkYWlseSBiYXNpcyAmIENGUiB0YWtlbiBvbiBsYXN0IGRhdGUgb2YgbW9udGggYnkgY2VsaW5nX2RhdGUoIm1vbnRoIikgLTEgZnVuY3Rpb24uDQoNCg0KIyMjIEV4cGVyaW1lbnQgLSBoaWdoY2hhcnRlciBtYXAgZGF0YQ0KDQpmcm9tOiANCg0KaHR0cHM6Ly9qa3Vuc3QuY29tL2hpZ2hjaGFydGVyL2FydGljbGVzL21hcHMuaHRtbA0KDQpodHRwczovL2NvZGUuaGlnaGNoYXJ0cy5jb20vDQoNCmh0dHBzOi8vY29kZS5oaWdoY2hhcnRzLmNvbS9tYXBkYXRhLw0KDQpodHRwczovL2prdW5zdC5jb20vaGlnaGNoYXJ0ZXIvcmVmZXJlbmNlL2Rvd25sb2FkX21hcF9kYXRhLmh0bWwNCg0KYGBge3IgZXZhbD1GQUxTRX0NCmhjbWFwKCJjb3VudHJpZXMvaW4vaW4tYWxsIikNCmBgYA0KDQoNCmBgYHtyfQ0KZGZfaW5kX21hcCA8LSBnZXRfZGF0YV9mcm9tX21hcChkb3dubG9hZF9tYXBfZGF0YSgiaHR0cHM6Ly9jb2RlLmhpZ2hjaGFydHMuY29tL21hcGRhdGEvY291bnRyaWVzL2luL2luLWFsbC5qcyIpKQ0KDQpnbGltcHNlKGRmX2luZF9tYXApDQpgYGANCg0KYGBge3J9DQpkZl9pbmRfbWFwICU+JSANCiAgc2VsZWN0KGBoYy1hMmAsIG5hbWUpDQpgYGANCg0KYGBge3J9DQpkYXRhX2Zha2UgPC0gZGZfaW5kX21hcCAlPiUgDQogIHNlbGVjdChjb2RlID0gImhjLWEyIikgJT4lIA0KICBtdXRhdGUodmFsdWUgPSAxZTYgKiBhYnMocnQobnJvdyguKSwgZGYgPSAxMCkpKQ0KDQpnbGltcHNlKGRhdGFfZmFrZSkNCmBgYA0KDQpgYGB7cn0NCmhjbWFwKA0KICAiY291bnRyaWVzL2luL2luLWFsbCIsDQogIGRhdGEgPSBkYXRhX2Zha2UsDQogIHZhbHVlID0gInZhbHVlIiwNCiAgam9pbkJ5ID0gYygiaGMtYTIiLCAiY29kZSIpLA0KICBuYW1lID0gIkZha2UgZGF0YSIsDQogIGRhdGFsYWJlbHMgPSBsaXN0KGVuYWJsZWQgPSBUUlVFLCBmb3JtYXQgPSAie3BvaW50Lm5hbWV9IiksDQogIGJvcmRlckNvbG9yID0gIiNGQUZBRkEiLA0KICBib3JkZXJXaWR0aCA9IDAuMSwNCiAgdG9vbHRpcCA9IGxpc3QoDQogICAgdmFsdWVEZWNpbWFscyA9IDIsDQogICAgdmFsdWVQcmVmaXggPSAiJCIpDQopDQpgYGANCg0KYGBge3J9DQppbnRlcnNlY3QoZGZfaW5kJFN0YXRlLlVuaW9uVGVycml0b3J5LCBkZl9pbmRfbWFwJG5hbWUpDQpgYGANCg0KYGBge3J9DQpkaW0oZGZfaW5kKQ0KYGBgDQoNCg0KYGBge3J9DQpkZl9pbmQgPC0gbGVmdF9qb2luKHggPSBkZl9pbmQsIHkgPSAoZGZfaW5kX21hcCAlPiUgc2VsZWN0KG5hbWUsYGhjLWEyYCkpLA0KICAgICAgICAgIGJ5ID0gYygiU3RhdGUuVW5pb25UZXJyaXRvcnkiID0gIm5hbWUiKSkNCg0KZGZfaW5kDQpgYGANCg0KYGBge3J9DQpkZl9pbmRbd2hpY2goaXMubmEoZGZfaW5kJGBoYy1hMmApKSwiU3RhdGUuVW5pb25UZXJyaXRvcnkiXSAlPiUgdW5pcXVlKCkNCmBgYA0KDQpUZWxhbmdhbmEsIExhZGFraCBjb3VsZG4ndCBiZSBmaW5kDQoNCmBgYHtyfQ0KZGZfaW5kW3doaWNoKGRmX2luZCRTdGF0ZS5VbmlvblRlcnJpdG9yeSA9PSAiRGFtYW4gJiBEaXUiKSwgImhjLWEyIl0gPC0gIkRBIg0KZGZfaW5kW3doaWNoKGRmX2luZCRTdGF0ZS5VbmlvblRlcnJpdG9yeSA9PSAiT2Rpc2hhIiksICJoYy1hMiJdIDwtICJPUiINCmRmX2luZFt3aGljaChkZl9pbmQkU3RhdGUuVW5pb25UZXJyaXRvcnkgPT0gIkRhZHJhIGFuZCBOYWdhciBIYXZlbGkgYW5kIERhbWFuIGFuZCBEaXUiKSwgImhjLWEyIl0gPC0gIkROIg0KZGZfaW5kW3doaWNoKGRmX2luZCRTdGF0ZS5VbmlvblRlcnJpdG9yeSA9PSAiVXR0YXJha2hhbmQiKSwgImhjLWEyIl0gPC0gIlVUIg0KYGBgDQoNCg0KYGBge3J9DQpoY21hcCgNCiAgImNvdW50cmllcy9pbi9pbi1hbGwiLA0KICBkYXRhID0gKGRmX2luZCAlPiUgZmlsdGVyKERhdGUgPT0gbWF4KERhdGUsIG5hLnJtID0gVFJVRSkpKSwNCiAgdmFsdWUgPSAiQ29uZmlybWVkIiwNCiAgam9pbkJ5ID0gYygiaGMtYTIiKSwNCiAgbmFtZSA9ICJDb25maXJtZWQgQ2FzZXMiLA0KICBkYXRhbGFiZWxzID0gbGlzdChlbmFibGVkID0gVFJVRSwgZm9ybWF0ID0gIntwb2ludC5uYW1lfSIpLA0KICBib3JkZXJDb2xvciA9ICIjRkFGQUZBIiwNCiAgYm9yZGVyV2lkdGggPSAwLjEsDQogIHRvb2x0aXAgPSBsaXN0KA0KICAgICMgdmFsdWVEZWNpbWFscyA9IDIsDQogICAgIyB2YWx1ZVByZWZpeCA9ICIkIg0KICAgICkNCikgJT4lIA0KICBoY190aXRsZSh0ZXh0ID0gIkNvbmZpcm1lZCBDYXNlcyBTdGF0ZXdpc2UgYWNyb3NzIEluZGlhIikNCmBgYA0KDQojIyMjIE1hcCBDaGFydHMgLSBDb25maXJtZWRQZXJjYXANCg0KYGBge3J9DQpoY21hcCgNCiAgImNvdW50cmllcy9pbi9pbi1hbGwiLA0KICBkYXRhID0gKGRmX2luZCAlPiUgDQogICAgICAgICAgICBmaWx0ZXIoRGF0ZSA9PSBtYXgoRGF0ZSwgbmEucm0gPSBUUlVFKSkgJT4lIA0KICAgICAgICAgICAgbXV0YXRlKENhc2VzUGVyY2FwUGVyYyA9IENvbmZpcm1lZC9Qb3B1bGF0aW9uX1N0YXRlICogMTAwKSksDQogIHZhbHVlID0gIkNhc2VzUGVyY2FwUGVyYyIsDQogIGpvaW5CeSA9IGMoImhjLWEyIiksDQogIG5hbWUgPSAiQ29uZmlybWVkIENhc2VzIFBlciBDYXBpdGEgKCUpIiwNCiAgZGF0YWxhYmVscyA9IGxpc3QoZW5hYmxlZCA9IFRSVUUsIGZvcm1hdCA9ICJ7cG9pbnQubmFtZX0iKSwNCiAgYm9yZGVyQ29sb3IgPSAiI0ZBRkFGQSIsDQogIGJvcmRlcldpZHRoID0gMC4xLA0KICB0b29sdGlwID0gbGlzdCgNCiAgICB2YWx1ZURlY2ltYWxzID0gMiwNCiAgICB2YWx1ZVN1ZmZpeCA9ICIgJSINCiAgICApDQopICU+JSANCiAgaGNfdGl0bGUodGV4dCA9ICJDb25maXJtZWQgQ2FzZXMgUGVyIENhcGl0YSglKSBTdGF0ZXdpc2UgYWNyb3NzIEluZGlhIikgJT4lIA0KICBoY19zdWJ0aXRsZSh0ZXh0ID0gIkRlbGhpIGhhcyBoaWdoZXN0IENvbmZpcm1lZCBDYXNlcyBQZXIgQ2FwaXRhKCUpIEluZGlhIikgJT4lIA0KICBoY19jYXB0aW9uKHRleHQgPSAiY3JlYXRlZCBieSBWaVNhISEiKQ0KDQpgYGANCg0KIyMjIyBNYXAgQ2hhcnRzIGNvbG9yLSBDb25maXJtZWRQZXJjYXANCg0KYGBge3J9DQpoY21hcCgNCiAgImNvdW50cmllcy9pbi9pbi1hbGwiLA0KICBkYXRhID0gKGRmX2luZCAlPiUgDQogICAgICAgICAgICBmaWx0ZXIoRGF0ZSA9PSBtYXgoRGF0ZSwgbmEucm0gPSBUUlVFKSkgJT4lIA0KICAgICAgICAgICAgbXV0YXRlKENhc2VzUGVyY2FwUGVyYyA9IENvbmZpcm1lZC9Qb3B1bGF0aW9uX1N0YXRlICogMTAwKSksDQogIHZhbHVlID0gIkNhc2VzUGVyY2FwUGVyYyIsDQogIGpvaW5CeSA9IGMoImhjLWEyIiksDQogIG5hbWUgPSAiQ29uZmlybWVkIENhc2VzIFBlciBDYXBpdGEgKCUpIiwNCiAgZGF0YWxhYmVscyA9IGxpc3QoZW5hYmxlZCA9IFRSVUUsIGZvcm1hdCA9ICJ7cG9pbnQubmFtZX0iKSwNCiAgYm9yZGVyQ29sb3IgPSAiI0ZBRkFGQSIsDQogIGJvcmRlcldpZHRoID0gMC4xLA0KICB0b29sdGlwID0gbGlzdCgNCiAgICB2YWx1ZURlY2ltYWxzID0gMiwNCiAgICB2YWx1ZVN1ZmZpeCA9ICIgJSINCiAgICApDQopICU+JQ0KICBoY19jb2xvckF4aXMoc3RvcHMgPSBjb2xvcl9zdG9wcygpKSAlPiUgDQogIGhjX3RpdGxlKHRleHQgPSAiQ29uZmlybWVkIENhc2VzIFBlciBDYXBpdGEoJSkgU3RhdGV3aXNlIGFjcm9zcyBJbmRpYSIpICU+JSANCiAgaGNfc3VidGl0bGUodGV4dCA9ICJEZWxoaSBoYXMgaGlnaGVzdCBDb25maXJtZWQgQ2FzZXMgUGVyIENhcGl0YSglKSBJbmRpYSIpICU+JSANCiAgaGNfY2FwdGlvbih0ZXh0ID0gImNyZWF0ZWQgYnkgVmlTYSEhIikNCg0KYGBgDQoNCiMjIyMgTWFwIENoYXJ0cyBjdXN0b20gY29sb3ItIENvbmZpcm1lZFBlcmNhcA0KDQpmcm9tOiBodHRwczovL3d3dy5kYXRhbm92aWEuY29tL2VuL2xlc3NvbnMvaGlnaGNoYXJ0LWludGVyYWN0aXZlLXdvcmxkLW1hcC1pbi1yLw0KDQpgYGB7cn0NCmhjbWFwKA0KICAiY291bnRyaWVzL2luL2luLWFsbCIsDQogIGRhdGEgPSAoZGZfaW5kICU+JSANCiAgICAgICAgICAgIGZpbHRlcihEYXRlID09IG1heChEYXRlLCBuYS5ybSA9IFRSVUUpKSAlPiUgDQogICAgICAgICAgICBtdXRhdGUoQ2FzZXNQZXJjYXBQZXJjID0gQ29uZmlybWVkL1BvcHVsYXRpb25fU3RhdGUgKiAxMDApKSwNCiAgdmFsdWUgPSAiQ2FzZXNQZXJjYXBQZXJjIiwNCiAgam9pbkJ5ID0gYygiaGMtYTIiKSwNCiAgbmFtZSA9ICJDb25maXJtZWQgQ2FzZXMgUGVyIENhcGl0YSAoJSkiLA0KICBkYXRhbGFiZWxzID0gbGlzdChlbmFibGVkID0gVFJVRSwgZm9ybWF0ID0gIntwb2ludC5uYW1lfSIpLA0KICBib3JkZXJDb2xvciA9ICIjRkFGQUZBIiwNCiAgYm9yZGVyV2lkdGggPSAwLjEsDQogIHRvb2x0aXAgPSBsaXN0KA0KICAgIHZhbHVlRGVjaW1hbHMgPSAyLA0KICAgIHZhbHVlU3VmZml4ID0gIiAlIg0KICAgICkNCikgJT4lDQogIGhjX2NvbG9yQXhpcyhtaW5Db2xvciA9ICJwdXJwbGUiLCBtYXhDb2xvciA9ICJyZWQiKSAlPiUgDQogIGhjX3RpdGxlKHRleHQgPSAiQ29uZmlybWVkIENhc2VzIFBlciBDYXBpdGEoJSkgU3RhdGV3aXNlIGFjcm9zcyBJbmRpYSIpICU+JSANCiAgaGNfc3VidGl0bGUodGV4dCA9ICJEZWxoaSBoYXMgaGlnaGVzdCBDb25maXJtZWQgQ2FzZXMgUGVyIENhcGl0YSglKSBJbmRpYSIpICU+JSANCiAgaGNfY2FwdGlvbih0ZXh0ID0gImNyZWF0ZWQgYnkgVmlTYSEhIikNCg0KYGBgDQoNCiMjIyMgTWFwIENoYXJ0cyAtIENGUg0KDQpgYGB7cn0NCmhjbWFwKA0KICAiY291bnRyaWVzL2luL2luLWFsbCIsDQogIGRhdGEgPSAoZGZfaW5kICU+JSANCiAgICAgICAgICAgIGZpbHRlcihEYXRlID09IG1heChEYXRlLCBuYS5ybSA9IFRSVUUpKSANCiAgICAgICAgICAgICksDQogIHZhbHVlID0gIkNGUiIsDQogIGpvaW5CeSA9IGMoImhjLWEyIiksDQogIG5hbWUgPSAiQ0ZSKCUpIiwNCiAgZGF0YWxhYmVscyA9IGxpc3QoZW5hYmxlZCA9IFRSVUUsIGZvcm1hdCA9ICJ7cG9pbnQubmFtZX0iKSwNCiAgYm9yZGVyQ29sb3IgPSAiI0ZBRkFGQSIsDQogIGJvcmRlcldpZHRoID0gMC4xLA0KICB0b29sdGlwID0gbGlzdCgNCiAgICB2YWx1ZURlY2ltYWxzID0gMiwNCiAgICB2YWx1ZVN1ZmZpeCA9ICIgJSINCiAgICApDQopICU+JSANCiAgaGNfdGl0bGUodGV4dCA9ICJDRlIoJSkgU3RhdGV3aXNlIGFjcm9zcyBJbmRpYSIpICU+JSANCiAgaGNfc3VidGl0bGUodGV4dCA9ICJQdW5qYWIsIE1haGFyYXNodHJhIGhhcyBoaWdoZXN0IENGUiglKSBJbmRpYSIpICU+JSANCiAgaGNfY2FwdGlvbih0ZXh0ID0gImNyZWF0ZWQgYnkgVmlTYSEhIikNCg0KYGBgDQoNCiMjIyMgTWFwIENoYXJ0cyAtIENGUg0KDQpgYGB7cn0NCmhjbWFwKA0KICAiY291bnRyaWVzL2luL2luLWFsbCIsDQogIGRhdGEgPSAoZGZfaW5kICU+JSANCiAgICAgICAgICAgIGZpbHRlcihEYXRlID09IG1heChEYXRlLCBuYS5ybSA9IFRSVUUpKSANCiAgICAgICAgICAgICksDQogIHZhbHVlID0gIkNGUiIsDQogIGpvaW5CeSA9IGMoImhjLWEyIiksDQogIG5hbWUgPSAiQ0ZSKCUpIiwNCiAgZGF0YWxhYmVscyA9IGxpc3QoZW5hYmxlZCA9IFRSVUUsIGZvcm1hdCA9ICJ7cG9pbnQubmFtZX0iKSwNCiAgYm9yZGVyQ29sb3IgPSAiI0ZBRkFGQSIsDQogIGJvcmRlcldpZHRoID0gMC4xLA0KICB0b29sdGlwID0gbGlzdCgNCiAgICB2YWx1ZURlY2ltYWxzID0gMiwNCiAgICB2YWx1ZVN1ZmZpeCA9ICIgJSINCiAgICApDQopICU+JSANCiAgaGNfY29sb3JBeGlzKHN0b3BzID0gY29sb3Jfc3RvcHMoKSkgJT4lDQogIGhjX3RpdGxlKHRleHQgPSAiQ0ZSKCUpIFN0YXRld2lzZSBhY3Jvc3MgSW5kaWEiKSAlPiUgDQogIGhjX3N1YnRpdGxlKHRleHQgPSAiUHVuamFiLCBNYWhhcmFzaHRyYSBoYXMgaGlnaGVzdCBDRlIoJSkgSW5kaWEiKSAlPiUgDQogIGhjX2NhcHRpb24odGV4dCA9ICJjcmVhdGVkIGJ5IFZpU2EhISIpDQoNCmBgYA0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQo=